1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/973b8d73409f/ changeset: 973b8d73409f user: carlfeberhard date: 2013-02-19 20:04:41 summary: browser tests: refactor framework into modules; add history panel tests, anon-user history panel tests affected #: 10 files diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/anon-history-tests.js --- /dev/null +++ b/test/casperjs/anon-history-tests.js @@ -0,0 +1,181 @@ +// 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 + }); + + 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 ){ + email = spaceghost.fixtureData.testUser.email; + password = spaceghost.fixtureData.testUser.password; +} + +var galaxyCookieName = 'galaxysession', + + nameSelector = 'div#history-name', + unnamedName = 'Unnamed history', + subtitleSelector = 'div#history-subtitle-area', + initialSizeStr = '0 bytes', + tagIconSelector = '#history-tag.icon-button', + annoIconSelector = '#history-annotate.icon-button', + //emptyMsgSelector = '#emptyHistoryMessage'; + emptyMsgSelector = '.infomessagesmall', + emptyMsgStr = "Your history is empty. Click 'Get Data' on the left pane to start", + + tooltipSelector = '.bs-tooltip', + anonNameTooltip = 'You must be logged in to edit your history name', + + editableTextClass = 'editable-text', + editableTextInputSelector = 'input#renaming-active'; + +var historyFrameInfo = {}, + testUploadInfo = {}; + + +// =================================================================== TESTS +// ------------------------------------------------------------------- anonymous new, history +// open galaxy - ensure not logged in +spaceghost.thenOpen( spaceghost.baseUrl, function(){ + var loggedInAs = spaceghost.user.loggedInAs(); + this.debug( 'loggedInAs: ' + loggedInAs ); + if( loggedInAs ){ this.logout(); } +}); + +// ------------------------------------------------------------------- check anon cookies +spaceghost.then( function testAnonCookies(){ + this.test.comment( 'session cookie for anon-user should be present and well formed' ); + var cookies = this.page.cookies; + this.debug( this.jsonStr( this.page.cookies ) ); + //??: what are 'well formed' values? + this.test.assert( cookies.length === 1, "Has one cookie" ); + var galaxyCookie = cookies[0]; + this.test.assert( galaxyCookie.name === galaxyCookieName, "Cookie named: " + galaxyCookieName ); + this.test.assert( !galaxyCookie.secure, "Cookie.secure is false" ); +}); + +// ------------------------------------------------------------------- check the empty history for well formedness +// grab the history frame bounds for mouse later tests +spaceghost.then( function(){ + historyFrameInfo = this.getElementInfo( 'iframe[name="galaxy_history"]' ); + //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); +}); + +spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){ + this.test.comment( 'history panel for anonymous user, new history' ); + this.withFrame( this.selectors.frames.history, function(){ + this.test.comment( "frame should have proper url and title: 'History'" ); + this.test.assertMatch( this.getCurrentUrl(), /\/history/, 'Found history frame url' ); + this.test.assertTitle( this.getTitle(), 'History', 'Found history frame title' ); + + this.test.comment( "history name should exist, be visible, and have text " + unnamedName ); + this.test.assertExists( nameSelector, nameSelector + ' exists' ); + this.test.assertVisible( nameSelector, 'History name is visible' ); + this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); + + this.test.comment( "history subtitle should display size and size should be 0 bytes" ); + this.test.assertExists( subtitleSelector, 'Found ' + subtitleSelector ); + this.test.assertVisible( subtitleSelector, 'History subtitle is visible' ); + this.test.assertSelectorHasText( subtitleSelector, initialSizeStr, + 'History subtitle has "' + initialSizeStr + '"' ); + + this.test.comment( "NO tags or annotations icons should be available for an anonymous user" ); + this.test.assertDoesntExist( tagIconSelector, 'Tag icon button not found' ); + this.test.assertDoesntExist( annoIconSelector, 'Annotation icon button not found' ); + + this.test.comment( "A message about the current history being empty should be displayed" ); + this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); + this.test.assertVisible( emptyMsgSelector, 'Empty history message is visible' ); + this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, + 'Message contains "' + emptyMsgStr + '"' ); + + this.test.comment( 'name should have a tooltip with info on anon-user name editing' ); + // mouse over to find tooltip + //NOTE!!: bounds are returned relative to containing frame - need to adjust using historyFrameInfo + //TODO: into conv. fn + var nameInfo = this.getElementInfo( nameSelector ); + //this.debug( 'nameInfo:' + this.jsonStr( nameInfo ) ); + this.page.sendEvent( 'mousemove', + historyFrameInfo.x + nameInfo.x + 1, historyFrameInfo.y + nameInfo.y + 1 ); + this.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); + this.test.assertSelectorHasText( tooltipSelector, anonNameTooltip ); + + this.test.comment( 'name should NOT be editable when clicked by anon-user' ); + this.test.assert( nameInfo.attributes[ 'class' ].indexOf( editableTextClass ) === -1, + "Name field is not class for editable text" ); + this.click( nameSelector ); + this.test.assertDoesntExist( editableTextInputSelector, "Clicking on name does not create an input" ); + }); +}); + +// ------------------------------------------------------------------- anon user can upload file +spaceghost.then( function testAnonUpload(){ + this.test.comment( 'anon-user should be able to upload files' ); + spaceghost.tools.uploadFile( '../../test-data/1.txt', function uploadCallback( _uploadInfo ){ + this.debug( 'uploaded HDA info: ' + this.jsonStr( _uploadInfo ) ); + var hasHda = _uploadInfo.hdaElement, + hasClass = _uploadInfo.hdaElement.attributes[ 'class' ], + hasOkClass = _uploadInfo.hdaElement.attributes[ 'class' ].indexOf( 'historyItem-ok' ) !== -1; + this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name ); + uploadInfo = _uploadInfo; + }); +}); +spaceghost.then( function testAnonUpload(){ + this.test.comment( "empty should be NO LONGER be displayed" ); + this.test.assertNotVisible( emptyMsgSelector, 'Empty history message is not visible' ); +}); + + +// ------------------------------------------------------------------- anon user can run tool on file + +// ------------------------------------------------------------------- anon user registers/logs in -> same history +spaceghost.user.loginOrRegisterUser( email, password ); +//??: why is a reload needed here? If we don't, loggedInAs === '' ... +spaceghost.thenOpen( spaceghost.baseUrl, function(){ + + this.test.comment( 'anon-user should login and be associated with previous history' ); + var loggedInAs = spaceghost.user.loggedInAs(); + this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' ); + + this.withFrame( this.selectors.frames.history, function(){ + var hdaInfo = this.historypanel.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid ); + this.test.assert( hdaInfo !== null, "After logging in - found a matching hda by name and hid" ); + if( hdaInfo ){ + this.test.assert( uploadInfo.hdaElement.attributes.id === hdaInfo.attributes.id, + "After logging in - found a matching hda by hda view id: " + hdaInfo.attributes.id ); + } + }); +}); + + +// =================================================================== +spaceghost.run( function(){ + this.test.done(); +}); diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/casperjs_runner.py --- a/test/casperjs/casperjs_runner.py +++ b/test/casperjs/casperjs_runner.py @@ -95,6 +95,8 @@ stderr_msg = process.stderr.readline() stderr_msg = self.strip_escape_codes( stderr_msg.strip() ) log.debug( '(%s): %s', rel_script_path, stderr_msg ) + # HACK: this is the last string displayed using the debug settings - afterwards it hangs + # so: bail on this string if stderr_msg.startswith( self.casper_done_str ): break @@ -117,7 +119,7 @@ self.exec_path, self.casper_info ) raise - self.handle_js_results( stdout_output ) + return self.handle_js_results( stdout_output ) def build_command_line( self, rel_script_path, *args, **kwargs ): """Build the headless browser command line list for subprocess. @@ -284,35 +286,61 @@ log.debug( '\n--------------- tearing down module' ) +test_user = { + 'email': 'test1@test.test', + 'password': '123456' +} + # ==================================================================== TESTCASE EXAMPLE -# these could be broken out into other files - shouldn't be necc. ATM -class UserTests( CasperJSTestCase ): +# these could be broken out into other py files - shouldn't be necc. ATM +class Test_01_User( CasperJSTestCase ): """TestCase that uses javascript and a headless browser to test dynamic pages. """ + #debug = True def test_10_registration( self ): """User registration tests: register new user, logout, attempt bad registrations. """ # all keywords will be compiled into a single JSON obj and passed to the server - self.run_js_script( 'registration-tests.js', self.env.url, - testuser={ 'email': 'test1@test.test', 'password': '123456' }) + self.run_js_script( 'registration-tests.js', + testuser=test_user ) + #TODO:?? could theoretically do db cleanup, checks here with SQLALX #TODO: have run_js_script return other persistant fixture data (uploaded files, etc.) def test_20_login( self ): """User log in tests. """ - self.run_js_script( 'login-tests.js', self.env.url, - testuser={ 'email': 'test1@test.test', 'password': '123456' }) + self.run_js_script( 'login-tests.js', + testuser=test_user ) -class ToolTests( CasperJSTestCase ): +class Test_02_Tools( CasperJSTestCase ): """(Minimal) casperjs tests for tools. """ #debug = True def test_10_upload( self ): """Tests uploading files """ - self.run_js_script( 'upload-tests.js' ) + self.run_js_script( 'upload-tests.js', + testuser=test_user ) + + +class Test_03_HistoryPanel( CasperJSTestCase ): + """(Minimal) casperjs tests for tools. + """ + #debug = True + def test_00_history_panel( self ): + """Test history panel basics (controls, structure, refresh, history options menu, etc.). + """ + self.run_js_script( 'history-panel-tests.js', + testuser=test_user ) + + def test_10_anonymous_histories( self ): + """Test history panel basics with an anonymous user. + """ + self.run_js_script( 'anon-history-tests.js', + testuser=test_user ) + # ==================================================================== MAIN diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/history-panel-tests.js --- /dev/null +++ b/test/casperjs/history-panel-tests.js @@ -0,0 +1,252 @@ +// 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 + }); + + spaceghost.start(); + +} catch( error ){ + console.debug( error ); + phantom.exit( 1 ); +} + + +// =================================================================== +/* TODO: +*/ +// =================================================================== globals and helpers +var email = spaceghost.user.getRandomEmail(), + password = '123456'; +if( spaceghost.fixtureData.testUser ){ + email = spaceghost.fixtureData.testUser.email; + password = spaceghost.fixtureData.testUser.password; +} +var newHistoryName = "Test History"; + +var nameSelector = 'div#history-name', + unnamedName = 'Unnamed history', + subtitleSelector = 'div#history-subtitle-area', + initialSizeStr = '0 bytes', + tagIconSelector = '#history-tag.icon-button', + annoIconSelector = '#history-annotate.icon-button', + //emptyMsgSelector = '#emptyHistoryMessage'; + emptyMsgSelector = '.infomessagesmall', + emptyMsgStr = "Your history is empty. Click 'Get Data' on the left pane to start", + + tooltipSelector = '.bs-tooltip', + nameTooltip = 'Click to rename history', + + editableTextClass = 'editable-text', + editableTextInputSelector = 'input#renaming-active'; + +var historyFrameInfo = {}, + testUploadInfo = {}; + + +// =================================================================== TESTS +// ------------------------------------------------------------------- start a new user +spaceghost.user.loginOrRegisterUser( email, password ); +//??: why is a reload needed here? If we don't, loggedInAs === '' ... +spaceghost.thenOpen( spaceghost.baseUrl, function(){ + var loggedInAs = spaceghost.user.loggedInAs(); + this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' ); +}); + +// ------------------------------------------------------------------- check structure of empty history +// grab the history frame bounds for mouse later tests +spaceghost.then( function(){ + historyFrameInfo = this.getElementInfo( 'iframe[name="galaxy_history"]' ); + //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); +}); + +spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){ + this.test.comment( 'history panel, new history' ); + this.withFrame( this.selectors.frames.history, function(){ + this.test.comment( "frame should have proper url and title: 'History'" ); + this.test.assertMatch( this.getCurrentUrl(), /\/history/, 'Found history frame url' ); + this.test.assertTitle( this.getTitle(), 'History', 'Found history frame title' ); + + this.test.comment( "history name should exist, be visible, and have text " + unnamedName ); + this.test.assertExists( nameSelector, nameSelector + ' exists' ); + this.test.assertVisible( nameSelector, 'History name is visible' ); + this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); + + this.test.comment( "history subtitle should display size and size should be 0 bytes" ); + this.test.assertExists( subtitleSelector, 'Found ' + subtitleSelector ); + this.test.assertVisible( subtitleSelector, 'History subtitle is visible' ); + this.test.assertSelectorHasText( subtitleSelector, initialSizeStr, + 'History subtitle has "' + initialSizeStr + '"' ); + + this.test.comment( "tags and annotation icons should be available" ); + this.test.assertExists( tagIconSelector, 'Tag icon button found' ); + this.test.assertExists( annoIconSelector, 'Annotation icon button found' ); + + this.test.comment( "A message about the current history being empty should be displayed" ); + this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); + this.test.assertVisible( emptyMsgSelector, 'Empty history message is visible' ); + this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, + 'Message contains "' + emptyMsgStr + '"' ); + + }); +}); + +// ------------------------------------------------------------------- name editing +spaceghost.then( function(){ + this.test.comment( 'history panel, editing the history name' ); + this.withFrame( this.selectors.frames.history, function(){ + this.test.comment( 'name should have a tooltip with proper info on name editing' ); + var nameInfo = this.getElementInfo( nameSelector ); + this.page.sendEvent( 'mousemove', + historyFrameInfo.x + nameInfo.x + 1, historyFrameInfo.y + nameInfo.y + 1 ); + this.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); + this.test.assertSelectorHasText( tooltipSelector, nameTooltip ); + + this.test.comment( 'name should be create an input when clicked' ); + this.test.assert( nameInfo.attributes[ 'class' ].indexOf( editableTextClass ) !== -1, + "Name field classed for editable text" ); + this.click( nameSelector ); + this.test.assertExists( editableTextInputSelector, "Clicking on name creates an input" ); + + this.test.comment( 'name should be editable by entering keys and pressing enter' ); + //NOTE: casperjs.sendKeys adds a click before and a selector.blur after sending - won't work here + //TODO: to conv. fn + this.page.sendEvent( 'keypress', newHistoryName ); + this.page.sendEvent( 'keypress', this.page.event.key.Enter ); + this.wait( 1000, function(){ + this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is ' + newHistoryName ); + this.test.assertDoesntExist( editableTextInputSelector, "Input disappears after pressing enter" ); + }); + }); +}); +spaceghost.then( function(){ + this.withFrame( this.selectors.frames.history, function(){ + this.test.comment( 'name should revert if user clicks away while editing' ); + this.click( nameSelector ); + this.page.sendEvent( 'keypress', "Woodchipper metagenomics, Fargo, ND" ); + + // click above the name input element + var inputInfo = this.getElementInfo( editableTextInputSelector ); + this.page.sendEvent( 'mousedown', + historyFrameInfo.x + inputInfo.x + 1, historyFrameInfo.y + inputInfo.y - 5 ); + + this.wait( 1000, function(){ + this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is STILL ' + newHistoryName ); + this.test.assertDoesntExist( editableTextInputSelector, "Input disappears after clicking away" ); + }); + }); +}); +spaceghost.then( function(){ + this.withFrame( this.selectors.frames.history, function(){ + this.test.comment( 'name should revert if user hits ESC while editing' ); + this.click( nameSelector ); + this.page.sendEvent( 'keypress', "Arsenic Bacteria" ); + + this.page.sendEvent( 'keypress', this.page.event.key.Escape ); + this.wait( 1000, function(){ + this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is STILL ' + newHistoryName ); + this.test.assertDoesntExist( editableTextInputSelector, "Input disappears after hitting ESC" ); + }); + }); +}); + + +// ------------------------------------------------------------------- check structure of NON empty history +/* +// upload file: 1.txt +spaceghost.then( function upload(){ + this.test.comment( 'anon-user should be able to upload files' ); + spaceghost.uploadFile( '../../test-data/1.txt', function uploadCallback( _uploadInfo ){ + this.debug( 'uploaded HDA info: ' + this.jsonStr( _uploadInfo ) ); + var hasHda = _uploadInfo.hdaElement, + hasClass = _uploadInfo.hdaElement.attributes[ 'class' ], + hasOkClass = _uploadInfo.hdaElement.attributes[ 'class' ].indexOf( 'historyItem-ok' ) !== -1; + this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name ); + uploadInfo = _uploadInfo; + }); +}); + +//TODO: for each uploaded file: 1 file per (standard) datatype (or some subset) +// txt, tabular, sam, bam, fasta, fastq, bed, gff, +*/ + +// ------------------------------------------------------------------- +//history panel + // structure of empty + // upload file + // structure of not empty + // tags + // annotation + // history refresh + // history options + // structure + + // deleted + + // hidden + + // persistant expansion (or in hdaView?) + +//hdaView +// with hpanel: + // (we assume hda is in the ok state) + // with collapsed hda: + // can we see the hid? + // can we see the title? + // three primary action buttons: + // they exist? + // Do they have good hrefs, targets? + // Are they enabled? + // do they have proper tooltips? + // display + // edit + // delete + //??: click through? + + // with expaned hda: + // can we see the hid, title, and primary display buttons? + + // misc info: no dbkey specified - is there a '?' link leading to edit attr? + // misc info: uploaded sam file + // misc info: format: sam + + // secondary actions: + // download + // info + // rerun + // visualizations + + // tags and annotations + //TODO: to their own file? tested elsewhere? + + // peek: + // proper headers? + // lines? + // scrollbar? + + // can re-collapse? + + + + + + + + +// =================================================================== +spaceghost.run( function(){ + this.test.done(); +}); diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/login-tests.js --- a/test/casperjs/login-tests.js +++ b/test/casperjs/login-tests.js @@ -32,7 +32,7 @@ */ // =================================================================== globals and helpers -var email = spaceghost.getRandomEmail(), +var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ email = spaceghost.fixtureData.testUser.email; @@ -40,51 +40,38 @@ } // =================================================================== TESTS +// register a user (again...) spaceghost.thenOpen( spaceghost.baseUrl, function(){ - this.test.comment( 'loading galaxy homepage' ); - // can we load galaxy? - this.test.assertTitle( 'Galaxy' ); + this.test.comment( 'registering: ' + email ); + spaceghost.user.registerUser( email, password ); }); -// ------------------------------------------------------------------- should work - -// register a user (again...) -spaceghost.then( function(){ - this.test.comment( 'registering: ' + email ); - spaceghost.registerUser( email, password ); -}); -// capture a sshot -//spaceghost.then( function(){ -// this.clickLabel( 'User' ); -// this.capture( 'register.png' ); -//}); - // log them out - check for empty logged in text spaceghost.then( function(){ this.test.comment( 'logging out: ' + email ); - spaceghost.logout(); + spaceghost.user.logout(); }); spaceghost.then( function(){ this.test.assertSelectorDoesntHaveText( xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), /\w/ ); - this.test.assert( spaceghost.loggedInAs() === '', 'loggedInAs() is empty string' ); + this.test.assert( spaceghost.user.loggedInAs() === '', 'loggedInAs() is empty string' ); }); // log them back in - check for email in logged in text spaceghost.then( function(){ this.test.comment( 'logging back in: ' + email ); - spaceghost._submitLogin( email, password ); //No such user + spaceghost.user._submitLogin( email, password ); //No such user }); -spaceghost.then( function(){ +spaceghost.thenOpen( spaceghost.baseUrl, function(){ this.test.assertSelectorHasText( xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), email ); - this.test.assert( spaceghost.loggedInAs() === email, 'loggedInAs() matches email' ); + this.test.assert( spaceghost.user.loggedInAs() === email, 'loggedInAs() matches email' ); }); // finally log back out for next tests spaceghost.then( function(){ this.test.comment( 'logging out: ' + email ); - spaceghost.logout(); + spaceghost.user.logout(); }); // ------------------------------------------------------------------- shouldn't work @@ -93,7 +80,7 @@ spaceghost.each( badEmails, function( self, badEmail ){ self.then( function(){ this.test.comment( 'attempting bad email: ' + badEmail ); - this._submitLogin( badEmail, password ); + this.user._submitLogin( badEmail, password ); }); self.then(function(){ this.assertErrorMessage( 'No such user' ); @@ -105,7 +92,7 @@ spaceghost.each( badPasswords, function( self, badPassword ){ self.then( function(){ this.test.comment( 'attempting bad password: ' + badPassword ); - this._submitLogin( email, badPassword ); + this.user._submitLogin( email, badPassword ); }); self.then(function(){ this.assertErrorMessage( 'Invalid password' ); @@ -118,7 +105,7 @@ this.assertStepsRaise( 'GalaxyError: LoginError', function(){ this.then( function(){ this.test.comment( 'testing (js) error thrown on bad email' ); - this.login( 'nihilist', '1234' ); + this.user.login( 'nihilist', '1234' ); }); }); }); @@ -127,15 +114,16 @@ this.assertStepsRaise( 'GalaxyError: LoginError', function(){ this.then( function(){ this.test.comment( 'testing (js) error thrown on bad password' ); - this.login( email, '1234' ); + this.user.login( email, '1234' ); }); }); }); spaceghost.then( function(){ - this.logout(); + this.user.logout(); }); - +/* +*/ // =================================================================== spaceghost.run( function(){ this.test.done(); diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/modules/historypanel.js --- /dev/null +++ b/test/casperjs/modules/historypanel.js @@ -0,0 +1,118 @@ +// =================================================================== module object, exports +/** Creates a new tools module object. + * @exported + */ +exports.create = function createHistoryPanel( spaceghost ){ + return new HistoryPanel( spaceghost ); +}; + +/** HistoryPanel object constructor. + * @param {SpaceGhost} spaceghost a spaceghost instance + */ +var HistoryPanel = function HistoryPanel( spaceghost ){ + this.options = { + progressIntervalDelay : 500 + }; + //??: circ ref? + this.spaceghost = spaceghost; +}; +exports.HistoryPanel = HistoryPanel; + +HistoryPanel.prototype.toString = function toString(){ + return this.spaceghost + '.HistoryPanel'; +}; + +// ------------------------------------------------------------------- +/* TODO: + run a tool + +*/ +// =================================================================== INTERNAL + +// =================================================================== API (external) +//TODO: to history module +/** Find the casper element info of the hda wrapper given the hda title and hid. + * NOTE: if more than one is found, will return the first found. + * precondition: you should wrap this with withFrame( 'galaxy_history' ) :( + * @param {String} title the title of the hda + * @param {Int} hid (optional) the hid of the hda to look for + * @returns {Object|null} ElementInfo of the historyItemWrapper found, null if not found + */ +HistoryPanel.prototype.hdaElementInfoByTitle = function hdaElementInfoByTitle( title, hid ){ + var spaceghost = this.spaceghost, + titleContains = ( hid !== undefined )?( hid + ': ' + title ):( title ), + wrapperInfo = null; + + //NOTE: depends on jquery + wrapperInfo = spaceghost.evaluate( function( titleContains ){ + // find the title, then the wrapper (2 containers up) + var $title = $( '.historyItemTitle:contains(' + titleContains + ')' ); + var $wrapper = $title.parent().parent(); + return (( $wrapper.attr( 'id' ) )?( __utils__.getElementInfo( '#' + $wrapper.attr( 'id' ) )):( null )); + }, titleContains ); + + return wrapperInfo; +}; + +/** 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) + * @param {String} hdaSelector selector for hda (should be historyItemWrapper) + * @param {String} finalState hda state to wait for (e.g. 'ok', 'error', 'running', 'queued', etc.) + * @param {Function} whenInStateFn called when hda goes into finalState + * @param {Function} timeoutFn called when maxWaitMs have passed without the desired state + * @param {Int} maxWaitMs number of milliseconds to wait before timing out (defaults to options.waitTimeout) + */ +HistoryPanel.prototype.waitForHdaState = function waitForHdaState( hdaSelector, finalState, + whenInStateFn, timeoutFn, maxWaitMs ){ + //TODO:?? explicitly a historyWrapper id? + // we need a larger timeout for these - it can take a bit + maxWaitMs = maxWaitMs || this.spaceghost.options.waitTimeout; + var spaceghost = this.spaceghost, + finalStateClass = '.historyItem-' + finalState; + + spaceghost.then( function(){ + spaceghost.withFrame( spaceghost.selectors.frames.history, function(){ + + // save the old time out + var oldWaitTimeout = spaceghost.options.waitTimeout, + + // output some progress indicator within the test (debug) + progressIntervalId = setInterval( function progress(){ + // get the state from the hda wrapper's class + var state = spaceghost.evaluate( function( hdaSelector ){ + if( !$( hdaSelector )[0] ){ return '(no hda found)'; } + var $wrapperClasses = $( hdaSelector ).attr( 'class' ); + //TODO: remove magic string/regex + return $wrapperClasses.match( /historyItem\-(\w+)/ )[1]; + }, hdaSelector ); + spaceghost.debug( hdaSelector + ': ' + state ); + }, spaceghost.historypanel.options.progressIntervalDelay ), + + // when done, close down the progress reporter and reset the wait timeout to what it was + finallyFn = function(){ + spaceghost.options.waitTimeout = oldWaitTimeout; + clearInterval( progressIntervalId ); + }; + + spaceghost.options.waitTimeout = maxWaitMs; + spaceghost.waitForSelector( hdaSelector + finalStateClass, + + // if the hda state became 'ok', call the whenInStateFn and close up + function _whenInState(){ + var hdaInfo = spaceghost.elementInfoOrNull( hdaSelector ); + spaceghost.debug( 'HDA now in state ' + finalState ); + //spaceghost.debug( 'HDA:\n' + hdaInfo ); + whenInStateFn.call( spaceghost, hdaInfo ); + finallyFn(); + + // if we've timed out, call the timeoutFn and close up + }, function timeout(){ + var hdaInfo = spaceghost.elementInfoOrNull( hdaSelector ); + spaceghost.debug( 'HDA timed out. HDA = ' + spaceghost.jsonStr( hdaInfo ) ); + timeoutFn.call( spaceghost, hdaInfo ); + finallyFn(); + } + ); + }); + }); +}; diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/modules/tools.js --- /dev/null +++ b/test/casperjs/modules/tools.js @@ -0,0 +1,221 @@ +// =================================================================== module object, exports +/** Creates a new tools module object. + * @exported + */ +exports.create = function createTools( spaceghost ){ + return new Tools( spaceghost ); +}; + +/** Tools object constructor. + * @param {SpaceGhost} spaceghost a spaceghost instance + */ +var Tools = function Tools( spaceghost ){ + //??: circ ref? + this.options = { + defaultUploadWait : ( 30 * 1000 ) + }; + this.spaceghost = spaceghost; +}; +exports.Tools = Tools; + +Tools.prototype.toString = function toString(){ + return this.spaceghost + '.Tools'; +}; + + +// =================================================================== INTERNAL +/** Tests uploading a file. + * NOTE: this version does NOT throw an error on a bad upload. + * It is meant for testing the upload functionality and, therefore, is marked as private. + * Other tests should use uploadFile + * @param {String} filepath the local filesystem path of the file to upload (absolute (?)) + */ +Tools.prototype._uploadFile = function _uploadFile( filepath ){ + var spaceghost = this.spaceghost, + uploadInfo = {}; + //TODO: check file exists using phantom.fs + //TODO: pull from test data + uploadInfo[ spaceghost.selectors.tools.upload.fileInput ] = filepath; + spaceghost.debug( 'uploading file: ' + filepath ); + + spaceghost.then( function(){ + spaceghost.withFrame( spaceghost.selectors.frames.tools, function(){ + spaceghost.clickLabel( spaceghost.labels.tools.upload.panelLabel ); + }); + }); + + spaceghost.then( function beginUpload(){ + spaceghost.withFrame( spaceghost.selectors.frames.main, function(){ + spaceghost.fill( spaceghost.selectors.tools.general.form, uploadInfo, false ); + + // the following throws: + // [error] [remote] Failed dispatching clickmouse event on xpath selector: //input[@value="Execute"]: + // PageError: TypeError: 'undefined' is not a function (evaluating '$(spaceghost).formSerialize()') + // ...and yet the upload still seems to work + spaceghost.click( xpath( spaceghost.selectors.tools.general.executeButton_xpath ) ); + }); + }); + + // debugging + spaceghost.withFrame( spaceghost.selectors.frames.main, function afterUpload(){ + var messageInfo = spaceghost.elementInfoOrNull( spaceghost.selectors.messages.all ); + spaceghost.debug( 'post upload message:\n' + spaceghost.jsonStr( messageInfo ) ); + }); +}; + +/** Uploads a file. + * @param {String} filepath the local filesystem path of the file to upload (absolute (?)) + */ +//Tools.prototype.uploadFile = function uploadFile( filepath ){ +// this._uploadFile( filepath ); +// this.then( function(){ +// this.withFrame( this.selectors.frames.main, function mainAfterUpload(){ +// var messageInfo = this.elementInfoOrNull( this.selectors.messages.all ); +// if( ( !messageInfo ) +// || ( messageInfo.attributes[ 'class' ] !== 'donemessagelarge' ) +// || ( messageInfo.text.indexOf( this.text.upload.success ) === -1 ) ){ +// throw new GalaxyError( 'UploadError: ' + this.jsonStr( messageInfo ) ); +// } +// }); +// }); +// return this; +//}; + +/** Parses the hid and name of a newly uploaded file from the tool execution donemessagelarge + * @param {String} doneMsgText the text extracted from the donemessagelarge after a tool execution + */ +Tools.prototype._parseDoneMessageForTool = function parseDoneMessageForTool( doneMsgText ){ + //TODO: test on non-upload + var executionInfo = {}; + var textMatch = doneMsgText.match( /added to the queue:\n\n(\d+)\: (.*)\n/m ); + if( textMatch ){ + if( textMatch.length > 1 ){ + executionInfo.hid = parseInt( textMatch[1], 10 ); + } + if( textMatch.length > 2 ){ + executionInfo.name = textMatch[2]; + } + executionInfo.name = textMatch[2]; + } + return executionInfo; +}; + +// ------------------------------------------------------------------- get avail. tools +// list available tools +//spaceghost.then( function(){ +// spaceghost.withFrame( 'galaxy_tools', function(){ +// //var availableTools = this.fetchText( 'a.tool-link' ); +// +// var availableTools = this.evaluate( function(){ +// //var toolTitles = __utils__.findAll( 'div.toolTitle' ); +// //return Array.prototype.map.call( toolTitles, function( e ){ +// // //return e.innerHtml; +// // return e.textContent || e.innerText; +// //}).join( '\n' ); +// +// var toolLinks = __utils__.findAll( 'a.tool-link' ); +// return Array.prototype.map.call( toolLinks, function( e ){ +// //return e.innerHtml; +// return e.textContent || e.innerText; +// }).join( '\n' ); +// }); +// this.debug( 'availableTools: ' + availableTools ); +// }); +//}); + +// =================================================================== API (external) +/** get filename from filepath + * @param {String} filepath (POSIX) filepath + * @returns {String} filename part of filepath + */ +Tools.prototype.filenameFromFilepath = function filenameFromFilepath( filepath ){ + var lastSepIndex = filepath.lastIndexOf( '/' ); + if( lastSepIndex !== -1 ){ + return filepath.slice( lastSepIndex + 1 ); + } + return filepath; +}; + +/** Wait for the hda with given id to move into the given state. + * callback function will be passed an uploadInfo object in the form: + * filepath: the filepath of the uploaded file + * filename: the filename of the uploaded file + * hid: the hid of the uploaded file hda in the current history + * name: the name of the uploaded file hda + * hdaElement: the hda DOM (casperjs form) element info object (see Casper#getElementInfo) + * @param {String} filepath (POSIX) filepath + * @param {Function} callback callback function called after hda moves into ok state (will be passed uploadInfo) + * @param {Integer} timeoutAfterMs milliseconds to wait before timing out (defaults to options.defaultUploadWait) + */ +Tools.prototype.uploadFile = function uploadFile( filepath, callback, timeoutAfterMs ){ + timeoutAfterMs = timeoutAfterMs || this.options.defaultUploadWait; + var spaceghost = this.spaceghost, + filename = this.filenameFromFilepath( filepath ), + uploadInfo = {}; + + // precondition: filepath is relative to scriptDir + filepath = spaceghost.options.scriptDir + '/' + filepath; + + // upload the file erroring if a done message is not displayed, aggregate info about upload + spaceghost.info( 'uploading file: ' + filepath + ' (timeout after ' + timeoutAfterMs + ')' ); + this._uploadFile( filepath ); + spaceghost.withFrame( spaceghost.selectors.frames.main, function toolExecuted(){ + spaceghost.debug( 'checking for done message' ); + var doneElementInfo = spaceghost.elementInfoOrNull( spaceghost.selectors.messages.donelarge ); + if( !doneElementInfo ){ + throw new spaceghost.GalaxyError( 'Upload Error: no done message uploading "' + filepath + '"' ); + } + spaceghost.debug( 'doneElementInfo: ' + spaceghost.jsonStr( doneElementInfo ) ); + // grab the hid and uploaded hda name from the done message + uploadInfo = spaceghost.tools._parseDoneMessageForTool( doneElementInfo.text ); + uploadInfo.filename = filename; + uploadInfo.filepath = filepath; + spaceghost.debug( 'uploadInfo: ' + spaceghost.jsonStr( uploadInfo ) ); + }); + + // 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.then( function getNewHda(){ + spaceghost.debug( 'beginning wait for upload file\'s ok state' ); + // get the hda view DOM element from the upload name and hid + spaceghost.withFrame( spaceghost.selectors.frames.history, function(){ + var hdaInfo = spaceghost.historypanel.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid ); + if( !hdaInfo ){ + throw new spaceghost.GalaxyError( 'Upload Error: uploaded file HDA not found: ' + + uploadInfo.hid + ', ' + uploadInfo.name ); + } + spaceghost.debug( 'hdaInfo: ' + spaceghost.jsonStr( hdaInfo ) ); + uploadInfo.hdaElement = hdaInfo; + // uploadInfo now has filepath, filename, name, hid, and hdaElement + }); + + spaceghost.then( function waitForOk(){ + spaceghost.debug( 'beginning wait for upload file\'s ok state' ); + + // currently best way to get hda state is using the historyItem-<state> class of the historyItemWrapper + var hdaStateClass = uploadInfo.hdaElement.attributes[ 'class' ].match( /historyItem\-(\w+)/ )[0]; + if( hdaStateClass !== 'historyItem-ok' ){ + + spaceghost.historypanel.waitForHdaState( '#' + uploadInfo.hdaElement.attributes.id, 'ok', + function whenInStateFn( hdaInfo ){ + // refresh hdaElement info + uploadInfo.hdaElement = hdaInfo; + callback.call( spaceghost, uploadInfo ); + + }, function timeoutFn( hdaInfo ){ + var finalClass = (( hdaInfo )?( hdaInfo.attributes[ 'class' ] ):( undefined )); + spaceghost.debug( 'final classes: ' + finalClass ); + throw new spaceghost.GalaxyError( 'Upload Error: timeout waiting for ok state: ' + + '"' + uploadInfo.hid + ': ' + uploadInfo.name + '"' + + ' (waited ' + timeoutAfterMs + ' ms)' ); + + }, timeoutAfterMs ); + } + }); + }); + return spaceghost; +}; +//TODO: upload via url +//TODO: upload by textarea + + diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/modules/user.js --- /dev/null +++ b/test/casperjs/modules/user.js @@ -0,0 +1,209 @@ +// =================================================================== module object, exports +/** Creates a new user module object. + * @exported + */ +exports.create = function createUser( spaceghost ){ + return new User( spaceghost ); +}; + +/** User object constructor. + * @param {SpaceGhost} spaceghost a spaceghost instance + */ +var User = function User( spaceghost ){ + //??: circ ref? + this.spaceghost = spaceghost; +}; +exports.User = User; + +User.prototype.toString = function toString(){ + return this.spaceghost + '.User'; +}; + + +// =================================================================== INTERNAL +/** 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. + * It is meant for testing the registration functionality and, therefore, is marked as private. + * Other tests should use registerUser + * @param {String} email the users email address + * @param {String} password the users password + * @param {String} username the users ...username! (optional: will use 1st part of email) + * @param {String} confirm password confirmation (optional: defaults to password) + */ +User.prototype._submitRegistration = function _submitRegistration( email, password, username, confirm ){ + var spaceghost = this.spaceghost, + userInfo = { + email : email, + password: password, + // default username to first part of email + username:( !username && email.match( /^\w*/ ) )?( email.match( /^\w*/ ) ):( username ), + // default confirm: duplicate of password + confirm : ( confirm !== undefined )?( confirm ):( password ) + }; + + spaceghost.debug( 'registering user:\n' + spaceghost.jsonStr( userInfo ) ); + spaceghost.thenOpen( spaceghost.baseUrl, function(){ + spaceghost.clickLabel( spaceghost.labels.masthead.menus.user ); + spaceghost.clickLabel( spaceghost.labels.masthead.userMenu.register ); + + spaceghost.withFrame( spaceghost.selectors.frames.main, function mainBeforeRegister(){ + spaceghost.debug( 'submitting registration... ' + spaceghost.getCurrentUrl() ); + spaceghost.fill( spaceghost.selectors.registrationPage.form, userInfo, false ); + // need manual submit (not a normal html form) + spaceghost.click( xpath( spaceghost.selectors.registrationPage.submit_xpath ) ); + }); + + //// debugging + //spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterRegister(){ + // var messageInfo = spaceghost.getElementInfo( spaceghost.selectors.messages.all ); + // spaceghost.debug( 'post registration message:\n' + spaceghost.jsonStr( messageInfo ) ); + //}); + }); +}; + +/** Tests logging in a user on the Galaxy instance by submitting the login form. + * NOTE: this version does NOT throw an error on a bad login. + * It is meant for testing the login functionality and, therefore, is marked as private. + * Other tests should use login + * @param {String} email the users email address + * @param {String} password the users password + */ +User.prototype._submitLogin = function logoutUser( email, password ){ + var spaceghost = this.spaceghost, + loginInfo = { + //NOTE: keys are used as name selectors in the fill fn - must match the names of the inputs + email: email, + password: password + }; + + spaceghost.thenOpen( spaceghost.baseUrl, function(){ + + spaceghost.clickLabel( spaceghost.labels.masthead.menus.user ); + spaceghost.clickLabel( spaceghost.labels.masthead.userMenu.login ); + + spaceghost.withFrame( spaceghost.selectors.frames.main, function mainBeforeLogin(){ + spaceghost.debug( '(' + spaceghost.getCurrentUrl() + ') logging in user:\n' + + spaceghost.jsonStr( loginInfo ) ); + spaceghost.fill( spaceghost.selectors.loginPage.form, loginInfo, false ); + spaceghost.click( xpath( spaceghost.selectors.loginPage.submit_xpath ) ); + }); + + //// debugging + //spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterLogin(){ + // //TODO: prob. could use a more generalized form of this for url breakdown/checking + // if( spaceghost.getCurrentUrl().search( spaceghost.selectors.loginPage.url_regex ) != -1 ){ + // var messageInfo = spaceghost.getElementInfo( spaceghost.selectors.messages.all ); + // spaceghost.debug( 'post login message:\n' + spaceghost.jsonStr( messageInfo ) ); + // } + //}); + }); +}; + + +// =================================================================== API (external) +/** Register a new user on the Galaxy instance. + * @param {String} email the users email address + * @param {String} password the users password + * @param {String} username the users ...username! (optional: will use 1st part of email) + * @returns {SpaceGhost} the spaceghost instance (for chaining) + */ +User.prototype.registerUser = function registerUser( email, password, username ){ + var spaceghost = this.spaceghost; + this._submitRegistration( email, password, username ); + spaceghost.then( function(){ + spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterRegister(){ + var messageInfo = spaceghost.getElementInfo( spaceghost.selectors.messages.all ); + spaceghost.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) ); + + if( messageInfo.attributes[ 'class' ] === 'errormessage' ){ + throw new spaceghost.GalaxyError( 'RegistrationError: ' + messageInfo.html ); + } + }); + }); + return spaceghost; +}; + +/** Logs in a user. Throws error on bad log in. + * @param {String} email the users email address + * @param {String} password the users password + * @returns {SpaceGhost} the spaceghost instance (for chaining) + */ +User.prototype.login = function login( email, password ){ + var spaceghost = this.spaceghost; + this._submitLogin( email, password ); + spaceghost.then( function(){ + spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterLogin(){ + if( spaceghost.getCurrentUrl().search( spaceghost.selectors.loginPage.url_regex ) != -1 ){ + var messageInfo = spaceghost.getElementInfo( spaceghost.selectors.messages.all ); + if( messageInfo && messageInfo.attributes[ 'class' ] === 'errormessage' ){ + throw new spaceghost.GalaxyError( 'LoginError: ' + messageInfo.html ); + } + } + }); + if( spaceghost.user.loggedInAs() === email ){ + spaceghost.debug( 'logged in as ' + email ); + } + }); + return spaceghost; +}; + +/** Fetch the email of the currently logged in user (or '' if not logged in) + * @returns {String} email of currently logged in user or '' if no one logged in + */ +User.prototype.loggedInAs = function loggedInAs(){ + var spaceghost = this.spaceghost, + userEmail = ''; + try { + var loggedInInfo = spaceghost.getElementInfo( xpath( spaceghost.selectors.masthead.userMenu.userEmail_xpath ) ); + userEmail = loggedInInfo.text; + } catch( err ){ + spaceghost.error( err ); + } + //console.debug( 'loggedInInfo:', spaceghost.jsonStr( loggedInInfo ) ); + return userEmail; +}; + +/** Log out the current user + * @returns {SpaceGhost} the spaceghost instance (for chaining) + */ +User.prototype.logout = function logoutUser(){ + var spaceghost = this.spaceghost; + spaceghost.thenOpen( spaceghost.baseUrl, function(){ + //TODO: handle already logged out + spaceghost.clickLabel( spaceghost.labels.masthead.menus.user ); + spaceghost.clickLabel( spaceghost.labels.masthead.userMenu.logout ); + }); + return spaceghost; +}; + +/** Attempts to login a user - if that raises an error (LoginError), register the user + * @param {String} email the users email address + * @param {String} password the users password + * @param {String} username the users ...username! (optional: will use 1st part of email) + * @returns {SpaceGhost} the spaceghost instance (for chaining) + */ +User.prototype.loginOrRegisterUser = function loginOrRegisterUser( email, password, username ){ + var spaceghost = this.spaceghost; + // attempt a login, if that fails - register + spaceghost.tryStepsCatch( function tryToLogin(){ + spaceghost.open( spaceghost.baseUrl ).user.login( email, password ); + + }, function failedLoginRegister(){ + spaceghost.open( spaceghost.baseUrl ).user.registerUser( email, password, username ); + }); + return spaceghost; +}; + +/** Gets a psuedo-random (unique?) email based on the time stamp. + * Helpful for testing registration. + * @param {String} username email user (defaults to 'test') + * @param {String} domain email domain (defaults to 'test.test') + * @returns {String} new email as string + */ +User.prototype.getRandomEmail = function getRandomEmail( username, domain ){ + username = username || 'test'; + domain = domain || 'test.test'; + return username + Date.now() + '@' + domain; +}; + + diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/registration-tests.js --- a/test/casperjs/registration-tests.js +++ b/test/casperjs/registration-tests.js @@ -24,7 +24,6 @@ phantom.exit( 1 ); } - // =================================================================== /* TODO: move selectors and assertText strings into global object for easier editing @@ -33,7 +32,7 @@ */ // =================================================================== globals and helpers -var email = spaceghost.getRandomEmail(), +var email = spaceghost.user.getRandomEmail(), password = '123456', confirm = password, username = 'test' + Date.now(); @@ -60,29 +59,27 @@ // ------------------------------------------------------------------- register a new user spaceghost.then( function(){ this.test.comment( 'registering user: ' + email ); - this._submitUserRegistration( email, password, username, confirm ); + this.user._submitRegistration( email, password, username, confirm ); }); spaceghost.thenOpen( spaceghost.baseUrl, function(){ this.clickLabel( 'User' ); this.test.assertSelectorHasText( 'a #user-email', email, '#user-email === ' + email ); }); - // ------------------------------------------------------------------- log out that user spaceghost.then( function(){ this.test.comment( 'logging out user: ' + email ); - this.logout(); + this.user.logout(); }); spaceghost.then( function(){ this.debug( 'email:' + this.getElementInfo( 'a #user-email' ).html ); this.test.assert( !this.getElementInfo( 'a #user-email' ).html, '#user-email is empty' ); }); - // ------------------------------------------------------------------- bad user registrations spaceghost.then( function(){ this.test.comment( 'attempting to re-register user: ' + email ); - this._submitUserRegistration( email, password, username, confirm ); + this.user._submitRegistration( email, password, username, confirm ); }); spaceghost.then(function(){ this.assertErrorMessage( 'User with that email already exists' ); @@ -93,7 +90,7 @@ spaceghost.each( badEmails, function( self, badEmail ){ self.then( function(){ this.test.comment( 'attempting bad email: ' + badEmail ); - this._submitUserRegistration( badEmail, password, username, confirm ); + this.user._submitRegistration( badEmail, password, username, confirm ); }); self.then(function(){ this.assertErrorMessage( 'Enter a real email address' ); @@ -105,7 +102,7 @@ spaceghost.each( badPasswords, function( self, badPassword ){ self.then( function(){ this.test.comment( 'attempting bad password: ' + badPassword ); - this._submitUserRegistration( spaceghost.getRandomEmail(), badPassword, username, confirm ); + this.user._submitRegistration( spaceghost.user.getRandomEmail(), badPassword, username, confirm ); }); self.then(function(){ this.assertErrorMessage( 'Use a password of at least 6 characters' ); @@ -117,7 +114,7 @@ spaceghost.each( badConfirms, function( self, badConfirm ){ self.then( function(){ this.test.comment( 'attempting bad password confirmation: ' + badConfirm ); - this._submitUserRegistration( spaceghost.getRandomEmail(), password, username, badConfirm ); + this.user._submitRegistration( spaceghost.user.getRandomEmail(), password, username, badConfirm ); }); self.then(function(){ this.assertErrorMessage( 'Passwords do not match' ); @@ -128,10 +125,10 @@ //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.getRandomEmail(), + var newEmail = spaceghost.user.getRandomEmail(), badUsername = 'bob'; this.test.comment( 'attempting short username: ' + badUsername ); - this._submitUserRegistration( newEmail, password, badUsername, confirm ); + this.user._submitRegistration( newEmail, password, badUsername, confirm ); }); spaceghost.then(function(){ this.assertErrorMessage( 'Public name must be at least 4 characters in length' ); @@ -141,9 +138,9 @@ var badUsernames = [ 'BOBERT', 'Robert Paulson', 'bobert!', 'bob_dobbs' ]; spaceghost.each( badUsernames, function( self, badUsername ){ self.then( function(){ - var newEmail = spaceghost.getRandomEmail(); + var newEmail = spaceghost.user.getRandomEmail(); this.test.comment( 'attempting bad username: ' + badUsername ); - this._submitUserRegistration( newEmail, password, badUsername, confirm ); + this.user._submitRegistration( newEmail, password, badUsername, confirm ); }); self.then(function(){ this.assertErrorMessage( "Public name must contain only lower-case letters, numbers and '-'" ); @@ -152,28 +149,29 @@ // ...and the name can't be used already spaceghost.then( function(){ - var newEmail = spaceghost.getRandomEmail(); + var newEmail = spaceghost.user.getRandomEmail(); this.test.comment( 'attempting previously used username with new user: ' + newEmail ); - this._submitUserRegistration( newEmail, password, username, confirm ); + this.user._submitRegistration( newEmail, password, username, confirm ); }); spaceghost.then(function(){ this.assertErrorMessage( 'Public name is taken; please choose another' ); }); -// ------------------------------------------------------------------- test the tests +// ------------------------------------------------------------------- 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.registerUser( '@internet', '123456', 'ignobel' ); + this.user.registerUser( '@internet', '123456', 'ignobel' ); }); }); }); spaceghost.then( function(){ - this.logout(); + //??: necessary? + this.user.logout(); }); // =================================================================== diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/spaceghost.js --- a/test/casperjs/spaceghost.js +++ b/test/casperjs/spaceghost.js @@ -72,6 +72,14 @@ //console.debug( 'CasperError:' + CasperError ); +// ------------------------------------------------------------------- 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 PageError.prototype = new CasperError(); PageError.prototype.constructor = CasperError; @@ -79,6 +87,7 @@ CasperError.apply( this, arguments ); this.name = "PageError"; } +SpaceGhost.prototype.PageError = PageError; GalaxyError.prototype = new CasperError(); GalaxyError.prototype.constructor = CasperError; @@ -86,6 +95,7 @@ CasperError.apply( this, arguments ); this.name = "GalaxyError"; } +SpaceGhost.prototype.GalaxyError = GalaxyError; AlertError.prototype = new CasperError(); AlertError.prototype.constructor = CasperError; @@ -93,11 +103,12 @@ 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 + * @param {Object} options option hash */ SpaceGhost.prototype.init = function init( options ){ //console.debug( 'init, options:', JSON.stringify( options, null, 2 ) ); @@ -122,6 +133,8 @@ ].concat( this.options.clientScripts ); this.debug( 'clientScripts:\n' + this.jsonStr( this.options.clientScripts ) ); + this._loadModules(); + }; /** Allow CLI arguments to set options if the proper option name is used. @@ -371,7 +384,16 @@ }; -// ------------------------------------------------------------------- page control +// ------------------------------------------------------------------- sub modules +/** Load sub modules (similar to casperjs.test) + */ +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 ); +}; + +// =================================================================== PAGE CONTROL /** An override of casper.open specifically for Galaxy. * (Currently only used to change language headers) */ @@ -530,327 +552,8 @@ }; + // =================================================================== GALAXY CONVENIENCE -/** Gets a psuedo-random (unique?) email based on the time stamp. - * Helpful for testing registration. - * @param {String} username email user (defaults to 'test') - * @param {String} domain email domain (defaults to 'test.test') - */ -SpaceGhost.prototype.getRandomEmail = function getRandomEmail( username, domain ){ - username = username || 'test'; - domain = domain || 'test.test'; - return username + Date.now() + '@' + domain; -}; - - -/** 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. - * It is meant for testing the registration functionality and, therefore, is marked as private. - * Other tests should use registerUser - * @param {String} email the users email address - * @param {String} password the users password - * @param {String} username the users ...username! (optional: will use 1st part of email) - * @param {String} confirm password confirmation (optional: defaults to password) - */ -SpaceGhost.prototype._submitUserRegistration = function _submitUserRegistration( email, password, username, confirm ){ - var userInfo = { - email : email, - password: password, - // default username to first part of email - username:( !username && email.match( /^\w*/ ) )?( email.match( /^\w*/ ) ):( username ), - // default confirm duplicate of password - confirm : ( confirm !== undefined )?( confirm ):( password ) - }; - this.debug( 'registering user:\n' + this.jsonStr( userInfo ) ); - - this.thenOpen( this.baseUrl, function(){ - - this.clickLabel( this.labels.masthead.menus.user ); - this.clickLabel( this.labels.masthead.userMenu.register ); - - this.withFrame( this.selectors.frames.main, function mainBeforeRegister(){ - this.debug( 'submitting registration... ' + this.getCurrentUrl() ); - this.fill( this.selectors.registrationPage.form, userInfo, false ); - // need manual up - this.click( xpath( this.selectors.registrationPage.submit_xpath ) ); - }); - - this.withFrame( this.selectors.frames.main, function mainAfterRegister(){ - var messageInfo = this.getElementInfo( this.selectors.messages.all ); - this.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) ); - }); - }); -}; - -/** Register a new user on the Galaxy instance. - * @param {String} email the users email address - * @param {String} password the users password - * @param {String} username the users ...username! (optional: will use 1st part of email) - */ -SpaceGhost.prototype.registerUser = function registerUser( email, password, username ){ - this._submitUserRegistration( email, password, username ); - this.then( function(){ - this.withFrame( this.selectors.frames.main, function mainAfterRegister(){ - var messageInfo = this.getElementInfo( this.selectors.messages.all ); - this.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) ); - - if( messageInfo.attributes[ 'class' ] === 'errormessage' ){ - throw new GalaxyError( 'RegistrationError: ' + messageInfo.html ); - } - }); - }); - return this; -}; - -/** Log out the current user - */ -SpaceGhost.prototype.logout = function logoutUser(){ - this.clickLabel( this.labels.masthead.menus.user ); - this.clickLabel( this.labels.masthead.userMenu.login ); - this.thenOpen( this.baseUrl, function(){ - //TODO: handle already logged out - this.clickLabel( this.labels.masthead.menus.user ); - this.clickLabel( this.labels.masthead.userMenu.logout ); - }); -}; - -/** Tests logging in a user on the Galaxy instance by submitting the login form. - * NOTE: this version does NOT throw an error on a bad login. - * It is meant for testing the login functionality and, therefore, is marked as private. - * Other tests should use login - * @param {String} email the users email address - * @param {String} password the users password - */ -SpaceGhost.prototype._submitLogin = function logoutUser( email, password ){ - var loginInfo = { - //NOTE: keys are used as name selectors in the fill fn - must match the names of the inputs - email: email, - password: password - }; - - this.thenOpen( this.baseUrl, function(){ - - this.clickLabel( this.labels.masthead.menus.user ); - this.clickLabel( this.labels.masthead.userMenu.login ); - - this.withFrame( this.selectors.frames.main, function mainBeforeLogin(){ - this.debug( '(' + this.getCurrentUrl() + ') logging in user:\n' + this.jsonStr( loginInfo ) ); - this.fill( this.selectors.loginPage.form, loginInfo, false ); - this.click( xpath( this.selectors.loginPage.submit_xpath ) ); - }); - this.withFrame( this.selectors.frames.main, function mainAfterLogin(){ - //TODO: prob. could use a more generalized form of this for url breakdown/checking - if( this.getCurrentUrl().search( this.selectors.loginPage.url_regex ) != -1 ){ - var messageInfo = this.getElementInfo( this.selectors.messages.all ); - this.debug( 'post login message:\n' + this.jsonStr( messageInfo ) ); - } - }); - }); -}; - -/** Logs in a user. Throws error on bad log in. - * @param {String} email the users email address - * @param {String} password the users password - */ -SpaceGhost.prototype.login = function login( email, password ){ - this._submitLogin( email, password ); - this.then( function(){ - this.withFrame( this.selectors.frames.main, function mainAfterLogin(){ - if( this.getCurrentUrl().search( this.selectors.loginPage.url_regex ) != -1 ){ - var messageInfo = this.getElementInfo( this.selectors.messages.all ); - if( messageInfo && messageInfo.attributes[ 'class' ] === 'errormessage' ){ - throw new GalaxyError( 'LoginError: ' + messageInfo.html ); - } - } - }); - if( this.loggedInAs() === email ){ - this.debug( 'logged in as ' + email ); - } - }); - return this; -}; - -/** Fetch the email of the currently logged in user (or '' if not logged in) - * @returns {String} email of currently logged in user or '' if no one logged in - */ -SpaceGhost.prototype.loggedInAs = function loggedInAs(){ - var userEmail = ''; - try { - var loggedInInfo = this.getElementInfo( xpath( this.selectors.masthead.userMenu.userEmail_xpath ) ); - userEmail = loggedInInfo.text; - } catch( err ){ - this.error( err ); - } - //console.debug( 'loggedInInfo:', this.jsonStr( loggedInInfo ) ); - return userEmail; -}; - -/** Attempts to login a user - if that raises an error (LoginError), register the user - * @param {String} email the users email address - * @param {String} password the users password - * @param {String} username the users ...username! (optional: will use 1st part of email) - */ -SpaceGhost.prototype.loginOrRegisterUser = function loginOrRegisterUser( email, password, username ){ - // attempt a login, if that fails - register - this.tryStepsCatch( function tryToLogin(){ - this.open( this.baseUrl ).login( email, password ); - - }, function failedLoginRegister(){ - this.open( this.baseUrl ).registerUser( email, password, username ); - }); - return this; -}; - -/** Tests uploading a file. - * NOTE: this version does NOT throw an error on a bad upload. - * It is meant for testing the upload functionality and, therefore, is marked as private. - * Other tests should use uploadFile - * @param {String} filepath the local filesystem path of the file to upload (absolute (?)) - */ -SpaceGhost.prototype._uploadFile = function _uploadFile( filepath ){ - var uploadInfo = {}; - //TODO: check file exists using phantom.fs - //TODO: pull from test data - uploadInfo[ this.tools.upload.fileInput ] = filepath; - this.debug( 'uploading file: ' + filepath ); - - spaceghost.then( function(){ - spaceghost.withFrame( this.selectors.frames.tools, function(){ - this.clickLabel( this.tools.upload.panelLabel ); - }); - }); - - this.then( function beginUpload(){ - spaceghost.withFrame( this.selectors.frames.main, function(){ - this.fill( this.tools.general.form, uploadInfo, false ); - - // the following throws: - // [error] [remote] Failed dispatching clickmouse event on xpath selector: //input[@value="Execute"]: - // PageError: TypeError: 'undefined' is not a function (evaluating '$(this).formSerialize()') - - // ...and yet the upload still seems to work - this.click( xpath( this.tools.general.executeButton_xpath ) ); - }); - }); - this.withFrame( this.selectors.frames.main, function afterUpload(){ - var messageInfo = this.elementInfoOrNull( this.selectors.messages.all ); - this.debug( 'post upload message:\n' + this.jsonStr( messageInfo ) ); - }); -}; - -/** Uploads a file. - * @param {String} filepath the local filesystem path of the file to upload (absolute (?)) - */ -SpaceGhost.prototype.uploadFile = function uploadFile( filepath ){ - this._uploadFile( filepath ); - this.then( function(){ - this.withFrame( this.selectors.frames.main, function mainAfterUpload(){ - var messageInfo = this.elementInfoOrNull( this.selectors.messages.all ); - if( ( !messageInfo ) - || ( messageInfo.attributes[ 'class' ] !== 'donemessagelarge' ) - || ( messageInfo.text.indexOf( this.text.upload.success ) === -1 ) ){ - throw new GalaxyError( 'UploadError: ' + this.jsonStr( messageInfo ) ); - } - }); - }); - return this; -}; - -/** Parses the hid and name of a newly uploaded file from the tool execution donemessagelarge - * @param {String} doneMsgText the text extracted from the donemessagelarge after a tool execution - */ -SpaceGhost.prototype.parseDoneMessageForTool = function parseDoneMessageForTool( doneMsgText ){ - //TODO: test on non-upload - var executionInfo = {}; - var textMatch = doneMsgText.match( /added to the queue:\n\n(\d+)\: (.*)\n/m ); - if( textMatch ){ - if( textMatch.length > 1 ){ - executionInfo.hid = parseInt( textMatch[1], 10 ); - } - if( textMatch.length > 2 ){ - executionInfo.name = textMatch[2]; - } - executionInfo.name = textMatch[2]; - } - return executionInfo; -}; - -/** Find the casper element info of the hda wrapper given the hda title and hid. - * NOTE: if more than one is found, will return the first found. - * precondition: you should wrap this with withFrame( 'galaxy_history' ) :( - * @param {String} title the title of the hda - * @param {Int} hid (optional) the hid of the hda to look for - * @returns {Object|null} ElementInfo of the historyItemWrapper found, null if not found - */ -SpaceGhost.prototype.hdaElementInfoByTitle = function hdaElementInfoByTitle( title, hid ){ - var spaceghost = this, - titleContains = ( hid !== undefined )?( hid + ': ' + title ):( title ), - wrapperInfo = null; - - wrapperInfo = spaceghost.evaluate( function( titleContains ){ - // find the title, then the wrapper (2 containers up) - var $title = $( '.historyItemTitle:contains(' + titleContains + ')' ); - var $wrapper = $title.parent().parent(); - return (( $wrapper.attr( 'id' ) )?( __utils__.getElementInfo( '#' + $wrapper.attr( 'id' ) )):( null )); - }, titleContains ); - - return wrapperInfo; -}; - -/** Wait for the hda with given id to move into the given state. - * @param {String} hdaSelector selector for hda (should be historyItemWrapper) - * @param {String} finalState hda state to wait for (e.g. 'ok', 'error', 'running', 'queued', etc.) - * @param {Function} whenInStateFn called when hda goes into finalState - * @param {Function} timeoutFn called when maxWaitMs have passed without the desired state - * @param {Int} maxWaitMs number of milliseconds to wait before timing out (defaults to options.waitTimeout) - */ -SpaceGhost.prototype.waitForHdaState = function waitForHdaState( hdaSelector, finalState, - whenInStateFn, timeoutFn, maxWaitMs ){ - //TODO:?? explicitly a historyWrapper id? - maxWaitMs = maxWaitMs || this.options.waitTimeout; - var finalStateClass = '.historyItem-' + finalState; - - this.then( function(){ - this.withFrame( this.selectors.frames.history, function(){ - // wait for state, preferrably debugging intermediate states - var spaceghost = this, - - // we need a larger timeout for these - it can take a bit - oldWaitTimeout = this.options.waitTimeout, - - // output some progress indicator within the test (debug) - progressIntervalId = setInterval( function progress(){ - var state = spaceghost.evaluate( function( hdaSelector ){ - var $wrapperClasses = $( hdaSelector ).attr( 'class' ); - return $wrapperClasses.match( /historyItem\-(\w+)/ )[1]; - }, hdaSelector ); - spaceghost.debug( hdaSelector + ': ' + state ); - }, 1000 ), - - // when done, close down the progress reporter and reset the wait timeout to what it was - finallyFn = function(){ - spaceghost.options.waitTimeout = oldWaitTimeout; - clearInterval( progressIntervalId ); - }; - - this.options.waitTimeout = maxWaitMs; - this.waitForSelector( hdaSelector + finalStateClass, function _whenInState(){ - this.debug( 'HDA now in state ' + finalState + ':\n' - + this.jsonStr( this.elementInfoOrNull( hdaSelector ) ) ); - whenInStateFn.call( this ); - finallyFn(); - - }, function timeout(){ - this.debug( 'timed out:\n' - + this.jsonStr( this.elementInfoOrNull( hdaSelector ) ) ); - timeoutFn.call( this ); - finallyFn(); - } - ); - }); - }); -}; - // =================================================================== MISCELAIN /** Send message to stderr */ @@ -958,9 +661,10 @@ history : 'galaxy_history' }, messages : { - all : '[class*="message"]', + all : '[class*="message"]', error : '.errormessage', - done : '.donemessage' + done : '.donemessage', + donelarge : '.donemessagelarge' }, loginPage : { form : 'form#login', @@ -970,6 +674,15 @@ registrationPage : { form : 'form#registration', submit_xpath : "//input[@value='Submit']" + }, + tools : { + general : { + form : 'form#tool_form', + executeButton_xpath : '//input[@value="Execute"]' + }, + upload : { + fileInput : 'files_0|file_data' // is this general? + } } }; @@ -983,17 +696,11 @@ login : 'Login', logout : 'Logout' } - } -}; - -SpaceGhost.prototype.tools = { - general : { - form : 'form#tool_form', - executeButton_xpath : '//input[@value="Execute"]' }, - upload : { - panelLabel : 'Upload File', - fileInput : 'files_0|file_data' // is this general? + tools : { + upload : { + panelLabel : 'Upload File' + } } }; @@ -1007,6 +714,12 @@ } }; +SpaceGhost.prototype.loadJSONFile = function loadJSONFile( filepath ){ + //precondition: filepath is relative to script dir + filepath = this.options.scriptDir + filepath; + return JSON.parse( require( 'fs' ).read( filepath ) ); +}; + // =================================================================== EXPORTS /** */ @@ -1021,10 +734,3 @@ return new SpaceGhost(options); }; -// ------------------------------------------------------------------- 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._ = _; diff -r e45b9ade65b1a27ded8d55a3003b153035551148 -r 973b8d73409ff4ebc33cc3ff6faf172417fb1978 test/casperjs/upload-tests.js --- a/test/casperjs/upload-tests.js +++ b/test/casperjs/upload-tests.js @@ -32,7 +32,7 @@ general tool execution */ // =================================================================== globals and helpers -var email = spaceghost.getRandomEmail(), +var email = spaceghost.user.getRandomEmail(), password = '123456'; if( spaceghost.fixtureData.testUser ){ email = spaceghost.fixtureData.testUser.email; @@ -42,44 +42,22 @@ // =================================================================== TESTS // ------------------------------------------------------------------- start a new user -spaceghost.loginOrRegisterUser( email, password ); +spaceghost.user.loginOrRegisterUser( email, password ); //??: why is a reload needed here? If we don't, loggedInAs === '' ... spaceghost.thenOpen( spaceghost.baseUrl, function(){ - var loggedInAs = spaceghost.loggedInAs(); + var loggedInAs = spaceghost.user.loggedInAs(); this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' ); }); -// ------------------------------------------------------------------- get avail. tools -// list available tools -//spaceghost.then( function(){ -// spaceghost.withFrame( 'galaxy_tools', function(){ -// //var availableTools = this.fetchText( 'a.tool-link' ); -// -// var availableTools = this.evaluate( function(){ -// //var toolTitles = __utils__.findAll( 'div.toolTitle' ); -// //return Array.prototype.map.call( toolTitles, function( e ){ -// // //return e.innerHtml; -// // return e.textContent || e.innerText; -// //}).join( '\n' ); -// -// var toolLinks = __utils__.findAll( 'a.tool-link' ); -// return Array.prototype.map.call( toolLinks, function( e ){ -// //return e.innerHtml; -// return e.textContent || e.innerText; -// }).join( '\n' ); -// }); -// this.debug( 'availableTools: ' + availableTools ); -// }); -//}); -// ------------------------------------------------------------------- upload from fs -// test uploading from the filesystem +// ------------------------------------------------------------------- long form +// upload a file from the filesystem var uploadInfo = {}; spaceghost.then( function(){ // strangely, this works with a non-existant file --> empty txt file var filename = '1.sam'; var filepath = this.options.scriptDir + '/../../test-data/' + filename; - this._uploadFile( filepath ); + this.tools._uploadFile( filepath ); // when an upload begins successfully... // 1. main should reload with a donemessagelarge @@ -90,7 +68,7 @@ this.test.assert( doneElementInfo !== null, "Found donemessagelarge after uploading file" ); - uploadInfo = this.parseDoneMessageForTool( doneElementInfo.text ); + uploadInfo = this.tools._parseDoneMessageForTool( doneElementInfo.text ); this.test.assert( uploadInfo.hid >= 0, 'Found sensible hid from upload donemessagelarge: ' + uploadInfo.hid ); this.test.assert( uploadInfo.name === filename, @@ -99,12 +77,12 @@ }); -// wait for upload to finish +// move to the history panel and wait for upload to finish spaceghost.then( function(){ var hdaInfo = null; this.withFrame( 'galaxy_history', function(){ - hdaInfo = this.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid ); + hdaInfo = this.historypanel.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid ); this.debug( 'hda:\n' + this.jsonStr( hdaInfo ) ); }); @@ -113,7 +91,7 @@ //precondition: needs class var hdaStateClass = hdaInfo.attributes[ 'class' ].match( /historyItem\-(\w+)/ )[0]; if( hdaStateClass !== 'historyItem-ok' ){ - this.waitForHdaState( '#' + hdaInfo.attributes.id, 'ok', + this.historypanel.waitForHdaState( '#' + hdaInfo.attributes.id, 'ok', function whenInStateFn(){ this.test.assert( true, 'Upload completed successfully for: ' + uploadInfo.name ); @@ -126,6 +104,34 @@ }); }); +spaceghost.then( function(){ + this.test.comment( 'testing convenience function' ); + spaceghost.tools.uploadFile( '../../test-data/1.sam', function( uploadInfo ){ + this.test.assert( uploadInfo.hdaElement !== null, "Convenience function produced hda in ok state" ); + }); +}); + +/* +//??: this error's AND waitFor()s THREE times - something to do with assertStepsRaise + waitFor +spaceghost.then( function(){ + this.test.comment( 'testing convenience function timeout error' ); + this.assertStepsRaise( 'GalaxyError: Upload Error: timeout waiting', function(){ + spaceghost.then( function(){ + spaceghost.tools.uploadFile( '../../test-data/1.sam', function( uploadInfo ){ + this.test.fail( "Convenience function did not timeout!" ); + }, 250 ); + }); + }); +}); + +// this correctly errors +spaceghost.then( function(){ + spaceghost.tools.uploadFile( '../../test-data/1.sam', function( uploadInfo ){ + this.test.fail( "Convenience function did not timeout!" ); + }, 250 ); +}); +*/ + // =================================================================== spaceghost.run( function(){ this.test.done(); 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.