commit/galaxy-central: carlfeberhard: Browser testing: add tests (and conv. fns) for working with the history options, history panel
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/0a6a4752493e/ changeset: 0a6a4752493e user: carlfeberhard date: 2013-03-05 21:35:59 summary: Browser testing: add tests (and conv. fns) for working with the history options, history panel affected #: 9 files diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/anon-history-tests.js --- a/test/casperjs/anon-history-tests.js +++ b/test/casperjs/anon-history-tests.js @@ -36,6 +36,7 @@ if( spaceghost.fixtureData.testUser ){ email = spaceghost.fixtureData.testUser.email; password = spaceghost.fixtureData.testUser.password; + spaceghost.info( 'Will use fixtureData.testUser: ' + email ); } var galaxyCookieName = 'galaxysession', @@ -174,6 +175,17 @@ }); }); +spaceghost.user.logout(); +spaceghost.thenOpen( spaceghost.baseUrl, function(){ + this.test.comment( 'logging out should create a new, anonymous history' ); + + this.withFrame( this.selectors.frames.history, function(){ + this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); + this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, + 'Message contains "' + emptyMsgStr + '"' ); + }); +}); + // =================================================================== spaceghost.run( function(){ diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/casperjs_runner.py --- a/test/casperjs/casperjs_runner.py +++ b/test/casperjs/casperjs_runner.py @@ -9,7 +9,7 @@ * sh run_functional_tests.sh test/casperjs/test_runner * sh run_functional_tests.sh -Note: that you can enable (lots) of debugging info using cli options: +Note: that you can enable (lots of) debugging info using cli options: * casperjs usertests.js --url='http://localhost:8080' --verbose=true --logLevel=debug (see casperjs.org for more information) @@ -94,11 +94,12 @@ while process.poll() == None: 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 + if stderr_msg: + 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 # stdout is assumed to have the json test data/results ( stdout_output, stderr_output ) = process.communicate() @@ -301,8 +302,12 @@ """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', - testuser=test_user ) + #self.run_js_script( 'registration-tests.js', + # # this causes a time out in history-panel-tests: why? + # # also: I can't seem to bump the timeout to an error (using a handler) - causes script to hang + # # removing for the sake of bbot + # testUser=test_user ) + self.run_js_script( 'registration-tests.js' ) #TODO:?? could theoretically do db cleanup, checks here with SQLALX #TODO: have run_js_script return other persistant fixture data (uploaded files, etc.) @@ -310,8 +315,7 @@ def test_20_login( self ): """User log in tests. """ - self.run_js_script( 'login-tests.js', - testuser=test_user ) + self.run_js_script( 'login-tests.js' ) class Test_02_Tools( CasperJSTestCase ): @@ -321,8 +325,7 @@ def test_10_upload( self ): """Tests uploading files """ - self.run_js_script( 'upload-tests.js', - testuser=test_user ) + self.run_js_script( 'upload-tests.js' ) class Test_03_HistoryPanel( CasperJSTestCase ): @@ -332,14 +335,12 @@ 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 ) + self.run_js_script( 'history-panel-tests.js' ) 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 ) + self.run_js_script( 'anon-history-tests.js' ) diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/history-panel-tests.js --- a/test/casperjs/history-panel-tests.js +++ b/test/casperjs/history-panel-tests.js @@ -27,6 +27,7 @@ // =================================================================== /* TODO: + possibly break this file up */ // =================================================================== globals and helpers var email = spaceghost.user.getRandomEmail(), @@ -34,8 +35,8 @@ if( spaceghost.fixtureData.testUser ){ email = spaceghost.fixtureData.testUser.email; password = spaceghost.fixtureData.testUser.password; + spaceghost.info( 'Will use fixtureData.testUser: ' + email ); } -var newHistoryName = "Test History"; var nameSelector = 'div#history-name', unnamedName = 'Unnamed history', @@ -43,7 +44,6 @@ 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", @@ -51,14 +51,33 @@ nameTooltip = 'Click to rename history', editableTextClass = 'editable-text', - editableTextInputSelector = 'input#renaming-active'; + editableTextInputSelector = 'input#renaming-active', -var historyFrameInfo = {}, + wrapperOkClassName = 'historyItem-ok', + + tagAreaSelector = '#history-tag-area', + annoAreaSelector = '#history-annotation-area', + refreshButtonSelector = 'a#history-refresh-button', + refreshButtonIconSelector = 'span.fa-icon-refresh', + refreshButtonHref = '/history', + + //historyOptionsButtonSelector = '#history-options-button', + //historyOptionsButtonIconSelector = 'span.fa-icon-cog', + includeDeletedOptionsLabel = spaceghost.historyoptions.data.labels.options.includeDeleted; + +function historyOptionXpathByLabel( label ){ + return xpath( '//ul[@id="history-options-button-menu"]/li/a[text()[contains(.,"' + label + '")]]' ); +} + +var newHistoryName = "Test History", + filepathToUpload = '../../test-data/1.txt', + historyFrameInfo = {}, testUploadInfo = {}; // =================================================================== TESTS -// ------------------------------------------------------------------- start a new user +// ------------------------------------------------------------------- set up +// 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(){ @@ -66,13 +85,13 @@ 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 ) ); }); +// ------------------------------------------------------------------- check structure of empty history spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){ this.test.comment( 'history panel, new history' ); this.withFrame( this.selectors.frames.history, function(){ @@ -100,7 +119,6 @@ this.test.assertVisible( emptyMsgSelector, 'Empty history message is visible' ); this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, 'Message contains "' + emptyMsgStr + '"' ); - }); }); @@ -163,88 +181,259 @@ }); }); - // ------------------------------------------------------------------- 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.test.comment( 'should be able to upload files' ); + spaceghost.tools.uploadFile( filepathToUpload, 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; + hasOkClass = _uploadInfo.hdaElement.attributes[ 'class' ].indexOf( wrapperOkClassName ) !== -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, -*/ +spaceghost.then( function checkPanelStructure(){ + this.test.comment( 'checking structure of non-empty panel' ); -// ------------------------------------------------------------------- -//history panel - // structure of empty - // upload file - // structure of not empty - // tags - // annotation - // history refresh - // history options - // structure + this.withFrame( this.selectors.frames.history, function(){ + 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, newHistoryName, 'History name is ' + newHistoryName ); - // deleted + this.test.comment( "history subtitle should display size and size should be " + onetxtFilesize + " bytes" ); + var onetxtFilesize = require( 'fs' ).size( this.options.scriptDir + filepathToUpload ), + expectedSubtitle = onetxtFilesize + ' bytes'; + this.test.assertExists( subtitleSelector, 'Found ' + subtitleSelector ); + this.test.assertVisible( subtitleSelector, 'History subtitle is visible' ); + this.test.assertSelectorHasText( subtitleSelector, expectedSubtitle, + 'History subtitle has "' + expectedSubtitle + '"' ); - // hidden + 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' ); - // persistant expansion (or in hdaView?) + this.test.comment( "A message about the current history being empty should NOT be displayed" ); + this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); + this.test.assertNotVisible( emptyMsgSelector, 'Empty history message is NOT visible' ); + }); +}); -//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? +// ------------------------------------------------------------------- tags +// keeping this light here - better for it's own test file +spaceghost.then( function openTags(){ + this.test.comment( 'tag area should open when the history panel tag icon is clicked' ); + this.withFrame( this.selectors.frames.history, function(){ + this.mouseEvent( 'click', tagIconSelector ); + this.wait( 1000, function(){ + this.test.assertVisible( tagAreaSelector, 'Tag area is now displayed' ); + }); + }); +}); +spaceghost.then( function closeTags(){ + this.test.comment( 'tag area should close when the history panel tag icon is clicked again' ); + this.withFrame( this.selectors.frames.history, function(){ + this.mouseEvent( 'click', tagIconSelector ); + this.wait( 1000, function(){ + this.test.assertNotVisible( tagAreaSelector, 'Tag area is now hidden' ); + }); + }); +}); - // with expaned hda: - // can we see the hid, title, and primary display buttons? +// ------------------------------------------------------------------- annotation +// keeping this light here - better for it's own test file +spaceghost.then( function openAnnotation(){ + this.test.comment( 'annotation area should open when the history panel annotation icon is clicked' ); + this.withFrame( this.selectors.frames.history, function(){ + this.mouseEvent( 'click', annoIconSelector ); + this.wait( 1000, function(){ + this.test.assertVisible( annoAreaSelector, 'Annotation area is now displayed' ); + }); + }); +}); +spaceghost.then( function closeAnnotation(){ + this.test.comment( 'annotation area should close when the history panel tag icon is clicked again' ); + this.withFrame( this.selectors.frames.history, function bler(){ + this.mouseEvent( 'click', annoIconSelector ); + this.wait( 1000, function(){ + this.test.assertNotVisible( annoAreaSelector, 'Tag area is now hidden' ); + }); + }); +}); - // misc info: no dbkey specified - is there a '?' link leading to edit attr? - // misc info: uploaded sam file - // misc info: format: sam +// ------------------------------------------------------------------- refresh button +spaceghost.then( function refreshButton(){ + this.test.comment( 'History panel should refresh when the history refresh icon is clicked' ); - // secondary actions: - // download - // info - // rerun - // visualizations + this.test.assertExists( refreshButtonSelector, "Found refresh button" ); + this.test.assertVisible( refreshButtonSelector, "Refresh button is visible" ); + this.test.assertVisible( refreshButtonSelector + ' ' + refreshButtonIconSelector, "Refresh icon is visible" ); + this.test.assert( this.getElementAttribute( refreshButtonSelector, 'href' ) === refreshButtonHref, + "Refresh button has href: " + refreshButtonHref ); - // tags and annotations - //TODO: to their own file? tested elsewhere? + this.assertNavigationRequested( refreshButtonHref, "History refreshed when clicking refresh icon", function(){ + this.click( refreshButtonSelector ); + }); +}); - // peek: - // proper headers? - // lines? - // scrollbar? +// ------------------------------------------------------------------- history options menu structure +//NOTE: options menu should be functionally tested elsewhere +spaceghost.then( function historyOptions(){ + this.test.comment( 'History options icon should be in place and menu should have the proper structure' ); - // can re-collapse? + // check the button and icon + this.test.assertExists( this.historyoptions.data.selectors.button, "Found history options button" ); + this.test.assertVisible( this.historyoptions.data.selectors.button, "History options button is visible" ); + this.test.assertVisible( this.historyoptions.data.selectors.buttonIcon, "History options icon is visible" ); + // open the menu + this.click( this.historyoptions.data.selectors.button ); + this.test.assertVisible( this.historyoptions.data.selectors.menu, + "Menu is visible when options button is clicked" ); + // check the options + for( var optionKey in this.historyoptions.data.labels.options ){ + if( this.historyoptions.data.labels.options.hasOwnProperty( optionKey ) ){ + var optionLabel = this.historyoptions.data.labels.options[ optionKey ], + optionXpath = this.historyoptions.data.selectors.optionXpathByLabelFn( optionLabel ); + this.test.assertVisible( optionXpath, 'Option label is visible: ' + optionLabel ); + } + } +}); +// ------------------------------------------------------------------- deleted hdas aren't in the dom +spaceghost.then( function(){ + this.test.comment( 'deleted hdas shouldn\'t be in the history panel DOM' ); + this.historypanel.deleteHda( '#' + uploadInfo.hdaElement.attributes.id, function(){ + this.test.assertDoesntExist( '#' + uploadInfo.hdaElement.attributes.id, "Deleted HDA is not in the DOM" ); + }); +}); +// ------------------------------------------------------------------- options allow showing/hiding deleted hdas +spaceghost.then( function(){ + this.test.comment( 'History options->' + includeDeletedOptionsLabel + ' shows deleted datasets' ); + this.historyoptions.includeDeleted(); + this.withFrame( this.selectors.frames.history, function(){ + this.waitForSelector( nameSelector, function(){ + this.test.assertExists( '#' + uploadInfo.hdaElement.attributes.id, + "Deleted HDA is in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" ); + this.test.assertVisible( '#' + uploadInfo.hdaElement.attributes.id, + "Deleted HDA is visible again (using history options -> " + includeDeletedOptionsLabel + ")" ); + }); + }); +}); +spaceghost.then( function(){ + this.test.comment( 'History options->' + includeDeletedOptionsLabel + ' (again) re-hides deleted datasets' ); + + this.historyoptions.includeDeleted(); + this.withFrame( this.selectors.frames.history, function(){ + this.waitForSelector( nameSelector, function(){ + this.test.assertDoesntExist( '#' + uploadInfo.hdaElement.attributes.id, + "Deleted HDA is not in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" ); + }); + }); +}); + +// undelete the uploaded file +spaceghost.then( function(){ + this.historyoptions.includeDeleted(); + this.withFrame( this.selectors.frames.history, function(){ + this.waitForSelector( nameSelector, function(){ + //TODO: to conv. fn + this.click( '#' + uploadInfo.hdaElement.attributes.id + ' .historyItemUndelete' ); + }); + }); +}); + +// ------------------------------------------------------------------- hidden hdas aren't shown +// ------------------------------------------------------------------- history options allows showing hidden hdas +// can't test this yet w/o a way to make hdas hidden thru the ui or api + +// ------------------------------------------------------------------- hdas can be expanded by clicking on the hda name +// broken in webkit w/ jq 1.7 +spaceghost.then( function(){ + this.test.comment( 'HDAs can be expanded by clicking on the name' ); + var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id; + + this.withFrame( this.selectors.frames.history, function(){ + this.click( uploadedSelector + ' .historyItemTitle' ); + this.debug( 'title: ' + this.debugElement( uploadedSelector + ' .historyItemTitle' ) ); + this.debug( 'wrapper: ' + this.debugElement( uploadedSelector ) ); + + this.wait( 1000, function(){ + this.test.assertExists( uploadedSelector + ' .historyItemBody', "Body for uploaded file is found" ); + this.test.assertVisible( uploadedSelector + ' .hda-summary', "hda-summary is visible" ); + }); + }); +}); + +// ------------------------------------------------------------------- expanded hdas are still expanded after a refresh +spaceghost.then( function(){ + this.test.comment( 'Expanded hdas are still expanded after a refresh' ); + var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id; + + this.click( refreshButtonSelector ); + this.withFrame( this.selectors.frames.history, function(){ + this.waitForSelector( nameSelector, function(){ + this.test.assertExists( uploadedSelector + ' .historyItemBody', "Body for uploaded file is found" ); + this.test.assertVisible( uploadedSelector + ' .hda-summary', "hda-summary is visible" ); + }); + }); + // this will break: webkit + jq 1.7 +}); + +// ------------------------------------------------------------------- expanded hdas collapse by clicking name again +spaceghost.then( function(){ + this.test.comment( 'Expanded hdas collapse by clicking name again' ); + var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id; + + this.withFrame( this.selectors.frames.history, function(){ + this.click( uploadedSelector + ' .historyItemTitle' ); + + this.wait( 500, function(){ + this.test.assertNotVisible( uploadedSelector + ' .hda-summary', "hda-summary is not visible" ); + }); + }); +}); + +// ------------------------------------------------------------------- collapsed hdas are still collapsed after a refresh +spaceghost.then( function(){ + this.test.comment( 'Expanded hdas are still expanded after a refresh' ); + var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id; + + this.click( refreshButtonSelector ); + this.withFrame( this.selectors.frames.history, function(){ + this.waitForSelector( nameSelector, function(){ + this.test.assertNotVisible( uploadedSelector + ' .hda-summary', "hda-summary is not visible" ); + }); + }); +}); + +// ------------------------------------------------------------------- history options collapses all expanded hdas +spaceghost.then( function(){ + // expand again + this.withFrame( this.selectors.frames.history, function(){ + this.click( '#' + uploadInfo.hdaElement.attributes.id + ' .historyItemTitle' ); + this.wait( 500, function(){}); + }); +}); +spaceghost.then( function(){ + this.test.comment( 'History option collapses all expanded hdas' ); + var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id; + + this.historyoptions.collapseExpanded(); + this.wait( 500, function(){ + this.withFrame( this.selectors.frames.history, function(){ + this.test.assertNotVisible( uploadedSelector + ' .hda-summary', "hda-summary is not visible" ); + }); + }); +}); // =================================================================== spaceghost.run( function(){ diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/modules/historyoptions.js --- /dev/null +++ b/test/casperjs/modules/historyoptions.js @@ -0,0 +1,160 @@ +// =================================================================== module object, exports +/** Creates a new historyoptions module object. + * @exported + */ +exports.create = function createHistoryOptions( spaceghost ){ + return new HistoryOptions( spaceghost ); +}; + +/** HistoryOptions object constructor. + * @param {SpaceGhost} spaceghost a spaceghost instance + */ +var HistoryOptions = function HistoryOptions( spaceghost ){ + //??: circ ref? + this.spaceghost = spaceghost; +}; +exports.HistoryOptions = HistoryOptions; + +HistoryOptions.prototype.toString = function toString(){ + return this.spaceghost + '.HistoryOptions'; +}; + +// ------------------------------------------------------------------- +/* TODO: + + +*/ +// =================================================================== API (external) +/** Just open the menu + */ +HistoryOptions.prototype.openMenu = function openMenu(){ + this.spaceghost.click( this.data.selectors.button ); +}; + +/** Click an option by Label + */ +HistoryOptions.prototype.clickOption = function clickOption( optionLabel ){ + this.openMenu(); + // casperjs clickLabel + var optionXpath = this.data.selectors.optionXpathByLabelFn( optionLabel ); + this.spaceghost.click( optionXpath ); +}; + +// ------------------------------------------------------------------- +// these options lead to controller pages - encapsulate those pages here +/** corresponds to history options menu: 'Saved Histories' + * @param {String} historyName the name of the history + */ +//HistoryOptions.prototype.savedHistoryByName = function savedHistoryByName( historyName ){ +//}; +/** corresponds to history options menu: 'Histories Shared with Me' + * @param {String} historyName the name of the history + */ +//HistoryOptions.prototype.sharedHistoryByName = function sharedHistoryByName( historyName ){ +//}; + +/** corresponds to history options menu: 'Create New' + */ +//HistoryOptions.prototype.createNew = function createNew(){ +//}; + +/** corresponds to history options menu: 'Copy History' + */ +//HistoryOptions.prototype.copyHistory = function copyHistory(){ +//}; + +/** corresponds to history options menu: 'Copy Datasets' + */ +//HistoryOptions.prototype.copyDatasets = function copyDatasets(){ +//}; + +/** corresponds to history options menu: 'Extract Workflow' + */ +//HistoryOptions.prototype.extractWorkflow = function extractWorkflow(){ +//}; + +/** corresponds to history options menu: 'Share or Publish' + */ +//HistoryOptions.prototype.shareHistoryViaLink = function shareHistoryViaLink(){ +//}; +/** corresponds to history options menu: 'Share or Publish' + */ +//HistoryOptions.prototype.publishHistory = function publishHistory(){ +//}; +/** corresponds to history options menu: 'Share or Publish' + */ +//HistoryOptions.prototype.shareHistoryWithUser = function shareHistoryWithUser(){ +//}; + +/** corresponds to history options menu: 'Dataset Security' + */ +//HistoryOptions.prototype.managePermissions = function managePermissions(){ +//}; +/** corresponds to history options menu: 'Dataset Security' + */ +//HistoryOptions.prototype.accessPermissions = function accessPermissions(){ +//}; + +/** corresponds to history options menu: 'Resume Paused Jobs' + */ +//HistoryOptions.prototype.resumePausedJobs = function resumePausedJobs(){ +//}; + + +// ------------------------------------------------------------------- +// these are easy, one click options (they don't open a new page) +/** corresponds to history options menu: 'Collapse Expanded Datasets' + */ +HistoryOptions.prototype.collapseExpanded = function collapseExpanded(){ + this.clickOption( this.data.labels.options.collapseExpanded ); +}; +/** corresponds to history options menu: 'Include Deleted Datasets' + */ +HistoryOptions.prototype.includeDeleted = function includeDeleted(){ + this.clickOption( this.data.labels.options.includeDeleted ); +}; +/** corresponds to history options menu: 'Include Hidden Datasets' + */ +HistoryOptions.prototype.includeHidden = function includeHidden(){ + this.clickOption( this.data.labels.options.includeHidden ); +}; + + +// =================================================================== SELECTORS +//TODO: data is not a very good name +HistoryOptions.prototype.data = { + selectors : { + button : '#history-options-button', + buttonIcon : '#history-options-button span.fa-icon-cog', + menu : '#history-options-button-menu', + optionXpathByLabelFn : function optionXpathByLabelFn( label ){ + return xpath( '//ul[@id="history-options-button-menu"]/li/a[text()[contains(.,"' + label + '")]]' ); + } + }, + labels : { + options : { + //History Lists + savedHistories : "Saved Histories", + sharedHistories : "Histories Shared with Me", + //Current History + createNew : "Create New", + copyHistory : "Copy History", + copyDatasets : "Copy Datasets", + shareOrPublish : "Share or Publish", + extractWorkflow : "Extract Workflow", + datasetSecurity : "Dataset Security", + resumePausedJobs : "Resume Paused Jobs", + collapseExpanded : 'Collapse Expanded Datasets', + includeDeleted : 'Include Deleted Datasets', + includeHidden : 'Include Hidden Datasets', + unhideHiddenDatasets : "Unhide Hidden Datasets", + purgeDeletedDatasets : "Purge Deleted Datasets", + showStructure : "Show Structure", + exportToFile : "Export to File", + deleteHistory : "Delete", + deleteHistoryPermanently : "Delete Permanently", + //Other Actions + importFromFile : "Import from File" + } + } +}; diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/modules/historypanel.js --- a/test/casperjs/modules/historypanel.js +++ b/test/casperjs/modules/historypanel.js @@ -1,5 +1,5 @@ // =================================================================== module object, exports -/** Creates a new tools module object. +/** Creates a new historypanel module object. * @exported */ exports.create = function createHistoryPanel( spaceghost ){ @@ -24,13 +24,15 @@ // ------------------------------------------------------------------- /* TODO: - run a tool + conv.fns: + expand hda (click title) + undelete hda + rename history */ // =================================================================== 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' ) :( @@ -116,3 +118,84 @@ }); }); }; + +/** 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 titleContains = ( hid !== undefined )?( hid + ': ' + title ):( title ), + wrapperInfo = this.spaceghost.elementInfoOrNull( + //TODO??: how to put this in editable json file + xpath( '//span[contains(text(),"' + titleContains + '")]/parent::*/parent::*' ) ); + //this.spaceghost.debug( 'wrapperInfo: ' + this.spaceghost.jsonStr( wrapperInfo ) ); + return wrapperInfo; +}; +//TODO!: this will break if the hda name has single or double quotes (which are permitted in names) + +/** Find the id of the hda wrapper given the hda title and hid. + * @param {String} title the title of the hda + * @param {Int} hid (optional) the hid of the hda to look for + * @returns {String|null} DOM id of the historyItemWrapper found, null if not found + */ +HistoryPanel.prototype.hdaIdByTitle = function hdaIdByTitle( title, hid ){ + var elementInfo = this.hdaElementInfoByTitle( title, hid ); + return (( elementInfo && elementInfo.attributes && elementInfo.attributes.id )? + ( elementInfo.attributes.id ):( null )); +}; + +/** 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. + * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper + * @param {Function} whenDeletedFn function to be called when the hda is deleted (optional) + * @param {Function} timeoutFn function to be called if/when the deleted attempted times out (optional) + */ +HistoryPanel.prototype.deleteHda = function deleteHda( hdaSelector, whenDeletedFn, timeoutFn ){ + whenDeletedFn = whenDeletedFn || function(){}; + var spaceghost = this.spaceghost; + + spaceghost.withFrame( spaceghost.selectors.frames.history, function deletingHda(){ + //precondition: historyItemWrapper's (hda dom elements) should have an id + // we could use the selector directly, but better if it errors before an attempted delete + var hdaId = spaceghost.getElementInfo( hdaSelector ).attributes.id; + spaceghost.debug( 'hda id: ' + spaceghost.jsonStr( hdaId ) ); + + // get the delete icon and click it + //TODO: handle disabled delete icon? + var deleteIconSelector = 'a[id^="historyItemDeleter-"]', + thisDeleteIconSelector = '#' + hdaId + ' ' + deleteIconSelector; + spaceghost.click( thisDeleteIconSelector ); + + spaceghost.waitWhileSelector( '#' + hdaId, + function hdaNoLongerInDom(){ + spaceghost.info( 'hda deleted: ' + hdaSelector ); + whenDeletedFn.call( spaceghost ); + + }, function timeout(){ + if( timeoutFn ){ + timeoutFn.call( spaceghost ); + } else { + throw new spaceghost.GalaxyError( 'HistoryPanelError: ' + + 'timeout attempting to delete hda : ' + hdaSelector ); + } + }); + }); +}; + +/** Expands an HDA. + * @param {String} hdaSelector a css or xpath selector for an historyItemWrapper + */ +HistoryPanel.prototype.expandHda = function expandHda( hdaSelector ){ + var spaceghost = this.spaceghost, + historyFrameInfo = spaceghost.getElementInfo( 'iframe[name="galaxy_history"]' ); + + spaceghost.withFrame( spaceghost.selectors.frames.history, function expandingHda(){ + var titleInfo = spaceghost.getElementInfo( hdaSelector + ' .historyItemTitle' ); + spaceghost.page.sendEvent( 'mousedown', + historyFrameInfo.x + titleInfo.x + 1, historyFrameInfo.y + titleInfo.y - 5 ); + }); + return spaceghost; +}; diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/modules/tools.js --- a/test/casperjs/modules/tools.js +++ b/test/casperjs/modules/tools.js @@ -154,7 +154,7 @@ uploadInfo = {}; // precondition: filepath is relative to scriptDir - filepath = spaceghost.options.scriptDir + '/' + filepath; + 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 + ')' ); @@ -179,14 +179,16 @@ 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.waitForSelector( '#history-name', function(){ + var hdaInfo = spaceghost.historypanel.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid ); + if( hdaInfo === null ){ + 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(){ @@ -217,5 +219,3 @@ }; //TODO: upload via url //TODO: upload by textarea - - diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/modules/user.js --- a/test/casperjs/modules/user.js +++ b/test/casperjs/modules/user.js @@ -68,7 +68,7 @@ * @param {String} email the users email address * @param {String} password the users password */ -User.prototype._submitLogin = function logoutUser( email, password ){ +User.prototype._submitLogin = function _submitLogin( 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 @@ -166,7 +166,7 @@ /** Log out the current user * @returns {SpaceGhost} the spaceghost instance (for chaining) */ -User.prototype.logout = function logoutUser(){ +User.prototype.logout = function logout(){ var spaceghost = this.spaceghost; spaceghost.thenOpen( spaceghost.baseUrl, function(){ //TODO: handle already logged out @@ -205,5 +205,3 @@ domain = domain || 'test.test'; return username + Date.now() + '@' + domain; }; - - diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/page-data/selectors.json --- /dev/null +++ b/test/casperjs/page-data/selectors.json @@ -0,0 +1,35 @@ +{ + "cookies" : { + "galaxyCookieName" : "galaxysession" + }, + + "selectors" : { + "historyPanel" : { + "name" : "div#history-name", + "subtitle" : "div#history-subtitle-area", + "tagIcon" : "#history-tag.icon-button", + "annoIcon" : "#history-annotate.icon-button", + "emptyMsg" : ".infomessagesmall" + }, + + "bootstrap" : { + "activeTooltip" : ".bs-tooltip" + }, + + "editableText" : { + "class" : "editable-text", + "activeInput" : "input#renaming-active" + } + }, + + "text" : { + "historyPanel" : { + "newName" : "Unnamed history", + "initialSizeStr" : "0 bytes", + "emptyMsgStr" : "Your history is empty. Click 'Get Data' on the left pane to start", + "tooltips" : { + "anonUserName" : "You must be logged in to edit your history name" + } + } + } +} diff -r 592af505e4e673aa891e9163bd237dbbc728ba62 -r 0a6a4752493eacfac2fb7a537d30be0333a58977 test/casperjs/spaceghost.js --- a/test/casperjs/spaceghost.js +++ b/test/casperjs/spaceghost.js @@ -2,6 +2,8 @@ Use in test command bug: assertStepsRaise raise errors (all the way) when used in 'casperjs test .' + normalize names of fns that use withFrame or then to 'then<action>' + make any callbacks optional (that can be) Does it run: casperjs usertests.js --url='http://localhost:8080' @@ -174,6 +176,8 @@ /** Set up any SG specific options passed in on the cli. */ SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){ + //this.debug( 'cli: ' + this.jsonStr( this.cli ) ); + //TODO: init these programmitically var CLI_OPTIONS = { returnJsonOnly : { defaultsTo: false, flag: 'return-json', help: 'send output to stderr, json to stdout' }, @@ -248,7 +252,7 @@ // 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( 1 ) )?( JSON.parse( this.cli.get( 1 ) ) ):( {} ); + this.fixtureData = ( this.cli.has( 0 ) )?( JSON.parse( this.cli.get( 0 ) ) ):( {} ); this.debug( 'fixtureData:' + this.jsonStr( this.fixtureData ) ); }; @@ -329,6 +333,14 @@ } }; +/** Event handler for step/casper timeouts - throws PageError + */ +SpaceGhost.prototype._timeoutHandler = function _timeoutHandler(){ + console.debug( 'timeout' ); + //msg = msg.replace( 'PageError: ', '' ); + throw new PageError( 'Timeout occurred' ); +}; + /** Event handler for console messages from the page. */ SpaceGhost.prototype._pageConsoleHandler = function _pageConsoleHandler(){ @@ -372,6 +384,9 @@ // ........................ page errors this.on( 'page.error', this._pageErrorHandler ); //this.on( 'load.failed', this._loadFailedHandler ); + this.on( 'timeout', this._timeoutHandler ); + this.on( 'step.timeout', this._timeoutHandler ); + this.on( 'waitFor.timeout', this._timeoutHandler ); // ........................ page info/debugging // these are already displayed at the casper info level @@ -391,6 +406,7 @@ 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 ); }; // =================================================================== PAGE CONTROL @@ -527,6 +543,32 @@ this.tryStepsCatch( stepsFn, testTheError ); }; +/** Assert that a function causes a navigation request with (at least partially) the given url. + * NOTE: _should_ play well with steps (e.g. then, thenOpen, etc.) + * @param {String} url some portion of the expected url for the nav request + * @param {String} message the assertion message + * @param {Function} fnThatRequests a function that causes a navigation request (e.g. click a link) + */ +SpaceGhost.prototype.assertNavigationRequested = function assertNavigationRequested( expectedUrl, message, + fnThatRequests ){ + var requested = false; + function captureNavReq( url, navigationType, navigationLocked, isMainFrame ){ + this.debug( 'Checking navigation.requested for url: ' + expectedUrl ); + // use || here to handle multiple requests, if any one url works -> test will pass + requested = requested || ( url.indexOf( expectedUrl ) !== -1 ); + } + this.then( function(){ + this.on( 'navigation.requested', captureNavReq ); + }); + this.then( function(){ + fnThatRequests.call( this ); + }); + this.then( function(){ + this.removeListener( 'navigation.requested', captureNavReq ); + this.test.assert( requested, message ); + }); +}; + // =================================================================== CONVENIENCE /** Wraps casper.getElementInfo in try, returning null if element not found instead of erroring. * @param {String} selector css or xpath selector for the element to find @@ -605,6 +647,12 @@ return JSON.stringify( obj, null, 2 ); }; +/** output to debug the JSON of the selector (or null if not found) + */ +SpaceGhost.prototype.debugElement = function debugElement( selector ){ + this.debug( this.jsonStr( this.elementInfoOrNull( selector ) ) ); +}; + /** Debug SG itself */ SpaceGhost.prototype.debugMe = function(){ @@ -720,6 +768,13 @@ return JSON.parse( require( 'fs' ).read( filepath ) ); }; +SpaceGhost.prototype.writeJSONFile = function writeJSONFile( filepath, object, mode ){ + mode = mode || 'w'; + //precondition: filepath is relative to script dir + filepath = this.options.scriptDir + filepath; + return require( 'fs' ).write( filepath, this.jsonStr( object ), mode ); +}; + // =================================================================== EXPORTS /** */ @@ -733,4 +788,3 @@ "use strict"; return new SpaceGhost(options); }; - Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.
participants (1)
-
commits-noreply@bitbucket.org