commit/galaxy-central: carlfeberhard: browser tests: add tests for structure of HDA in the 'ok' state
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/c2f310489973/ changeset: c2f310489973 user: carlfeberhard date: 2013-03-13 21:03:31 summary: browser tests: add tests for structure of HDA in the 'ok' state affected #: 4 files diff -r 4827bed265e7563c58346654f5a668f5de6d9d68 -r c2f310489973fc2ba42e5289ccec3ed88a42aabc test/casperjs/hda-state-tests.js --- a/test/casperjs/hda-state-tests.js +++ b/test/casperjs/hda-state-tests.js @@ -40,22 +40,22 @@ spaceghost.info( 'Will use fixtureData.testUser: ' + email ); } -var newHistoryName = "Test History", +var tooltipSelector = '.bs-tooltip'; + +var utils = require( 'utils' ), historyFrameInfo = {}, filepathToUpload = '../../test-data/1.txt', - possibleHDAStates = [], - testUploadInfo = {}; + testUploadInfo = {}, + //TODO: get from the api module - that doesn't exist yet + summaryShouldBeArray = [ '10 lines', 'format: txt' ], + infoShouldBe = 'uploaded txt file', + peekShouldBeArray = []; // ------------------------------------------------------------------- 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(){ - var loggedInAs = spaceghost.user.loggedInAs(); - this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' ); -}); -// grab the history frame bounds for mouse later tests +// grab the history frame bounds for later mouse tests spaceghost.then( function(){ historyFrameInfo = this.getElementInfo( 'iframe[name="galaxy_history"]' ); //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); @@ -69,15 +69,211 @@ }); }); -spaceghost.then( function getHDAStates(){ + +// =================================================================== TESTS +// ------------------------------------------------------------------- helpers +//NOTE: to be called with fn.call( spaceghost, ... ) + +function testTitle( hdaSelector, hid, name ){ + var titleSelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.title, + titleShouldBe = hid + ': ' + name; + this.test.assertVisible( titleSelector, + 'HDA title is visible' ); + this.test.assertSelectorHasText( titleSelector, titleShouldBe, + 'HDA has proper hid and title' ); +} + +function testTitleButtonStructure( hdaSelector, shouldHaveTheseButtons ){ + // defaults to the current buttons most states should have + shouldHaveTheseButtons = shouldHaveTheseButtons || [ 'display', 'edit', 'delete' ]; + + var hdaDbId = this.getElementAttribute( hdaSelector, 'id' ).split( '-' )[1], + buttonsArea = hdaSelector + ' ' + this.historypanel.data.selectors.hda.titleButtons, + buttons = { + // this seems backwards -> TODO: move buttonsArea concat into loop below, move this data to historypanel.data + display : { + nodeName : this.historypanel.data.text.hda.ok.nodeNames.displayButton, + selector : buttonsArea + ' ' + this.historypanel.data.selectors.hda.displayButton, + tooltip : this.historypanel.data.text.hda.ok.tooltips.displayButton, + hrefTpl : this.historypanel.data.text.hda.ok.hrefs.displayButton + }, + edit : { + nodeName : this.historypanel.data.text.hda.ok.nodeNames.editAttrButton, + selector : buttonsArea + ' ' + this.historypanel.data.selectors.hda.editAttrButton, + tooltip : this.historypanel.data.text.hda.ok.tooltips.editAttrButton, + hrefTpl : this.historypanel.data.text.hda.ok.hrefs.editAttrButton + }, + 'delete' : { + nodeName : this.historypanel.data.text.hda.ok.nodeNames.deleteButton, + selector : buttonsArea + ' ' + this.historypanel.data.selectors.hda.deleteButton, + tooltip : this.historypanel.data.text.hda.ok.tooltips.deleteButton, + hrefTpl : this.historypanel.data.text.hda.ok.hrefs.deleteButton + } + }; + this.test.assertVisible( buttonsArea, 'Button area is visible' ); + + for( var i=0; i<shouldHaveTheseButtons.length; i++ ){ + // don't use button names we don't have data for + var buttonName = shouldHaveTheseButtons[ i ]; + if( !buttons.hasOwnProperty( buttonName ) ){ continue; } + + this.test.comment( buttonName + ' should exist, be visible, and well formed' ); + var button = buttons[ buttonName ]; + this.debug( 'checking button "' + buttonName + '" on hda "' + hdaDbId + '":\n' + this.jsonStr( button ) ); + this.test.assertExists( button.selector, buttonName + ' button exists' ); + this.test.assertVisible( button.selector, buttonName + ' button is visible' ); + + var buttonElement = this.getElementInfo( button.selector ); + this.debug( 'buttonElement:' + this.jsonStr( buttonElement ) ); + + // should be an anchor + this.test.assert( buttonElement.nodeName === button.nodeName, + buttonName + ' is proper node type (' + button.nodeName + '): ' + buttonElement.nodeName ); + + // should have a proper href + var href = buttonElement.attributes.href, + hrefShouldBe = utils.format( button.hrefTpl, hdaDbId ); + this.assertTextContains( href, hrefShouldBe, + buttonName + ' has proper href (' + hrefShouldBe + '): ' + href ); + + this.historypanel.hoverOver( button.selector, function testingHover(){ + var tooltipText = button.tooltip; + this.test.assertVisible( tooltipSelector, buttonName + ' button tooltip is visible when hovering' ); + this.test.assertSelectorHasText( tooltipSelector, tooltipText, + buttonName + ' button has tooltip text: "' + tooltipText + '"' ); + }, historyFrameInfo ); + } +} + +function testDbkey( hdaSelector, dbkeySetTo ){ + var dbkeySelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.body + + ' ' + this.historypanel.data.selectors.hda.dbkey, + unspecifiedDbkeyText = '?', + unspecifiedDbkeyNodeName = 'a', + specifiedDbkeyNodeName = 'span', + editAttrHrefRegex = /\/datasets\/\w+\/edit/; + + this.test.assertExists( dbkeySelector, 'dbkey exists' ); + this.test.assertVisible( dbkeySelector, 'dbkey is visible' ); + var dbkey = this.elementInfoOrNull( dbkeySelector ); + if( !dbkey ){ return; } + + // dbkey is set, check text + if( dbkeySetTo ){ + this.test.comment( '(specified) dbkey should be displayed correctly' ); + this.test.assertSelectorHasText( dbkeySelector, dbkeySetTo, + 'dbkey is specified: ' + dbkey.text ); + this.test.assert( dbkey.nodeName === specifiedDbkeyNodeName, + 'dbkey has proper nodeName (' + specifiedDbkeyNodeName + '):' + dbkey.nodeName ); + + // dbkey expected to be not set + } else { + this.test.comment( '(unspecified) dbkey should be displayed correctly' ); + this.test.assertSelectorHasText( dbkeySelector, unspecifiedDbkeyText, + 'dbkey is not specified: ' + dbkey.text ); + this.test.assert( dbkey.nodeName === unspecifiedDbkeyNodeName, + 'dbkey has proper nodeName (' + unspecifiedDbkeyNodeName + '):' + dbkey.nodeName ); + + this.test.comment( '(unspecified) dbkey href should point to edit attributes' ); + this.test.assertMatch( dbkey.attributes.href, editAttrHrefRegex, + 'dbkey has a proper href: ' + dbkey.attributes.href ); + } +} + +function testPrimaryActionButtons( hdaSelector ){ + var buttonsSelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.body + + ' ' + this.historypanel.data.selectors.hda.primaryActionButtons; + this.test.comment( 'Primary action buttons div should exist and be visible' ); + this.test.assertExists( buttonsSelector, 'Primary action buttons div exists' ); + this.test.assertVisible( buttonsSelector, 'Primary action buttons div is visible' ); +} + +function testSecondaryActionButtons( hdaSelector ){ + var buttonsSelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.body + + ' ' + this.historypanel.data.selectors.hda.secondaryActionButtons; + this.test.comment( 'Secondary action buttons div should exist and be visible' ); + this.test.assertExists( buttonsSelector, 'Secondary action buttons div exists' ); + this.test.assertVisible( buttonsSelector, 'Secondary action buttons div is visible' ); +} + +function testPeek( hdaSelector, expectedPeekArray ){ + var peekSelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.body + + ' ' + this.historypanel.data.selectors.hda.peek; + this.test.comment( 'Peek div should exist and be visible' ); + this.test.assertExists( peekSelector, 'peek exists' ); + this.test.assertVisible( peekSelector, 'peek is visible' ); + expectedPeekArray.forEach( function( string, i ){ + spaceghost.test.assertSelectorHasText( peekSelector, string, 'peek has proper text (' + string + ')' ); + }); +} + +function testExpandedBody( hdaSelector, expectedSummaryTextArray, expectedInfoText, dbkeySetTo ){ + var body = hdaSelector + ' ' + this.historypanel.data.selectors.hda.body; + this.test.assertExists( body, 'body exists' ); + this.test.assertVisible( body, 'body is visible' ); + + //TODO: create api module, match with api history_contents + + this.test.comment( 'Summary should be displayed correctly' ); + var summary = body + ' ' + this.historypanel.data.selectors.hda.summary; + this.test.assertExists( summary, 'summary exists' ); + this.test.assertVisible( summary, 'summary is visible' ); + // summary text is broken up by whitespace making it inconv. to test in one go + expectedSummaryTextArray.forEach( function( string, i ){ + spaceghost.test.assertSelectorHasText( summary, string, 'summary has proper text (' + string + ')' ); + }); + this.debug( 'summary text: ' + this.fetchText( summary ) ); + + testDbkey.call( this, hdaSelector, dbkeySetTo ); + + this.test.comment( 'Info should be displayed correctly' ); + var info = body + ' ' + this.historypanel.data.selectors.hda.info; + this.test.assertExists( info, 'info exists' ); + this.test.assertVisible( info, 'info is visible' ); + this.test.assertSelectorHasText( info, expectedInfoText, + 'info has proper text (' + expectedInfoText + '): ' + this.fetchText( info ) ); + + testPrimaryActionButtons.call( this, hdaSelector ); + testSecondaryActionButtons.call( this, hdaSelector ); //TODO: isAnonymous + testPeek.call( this, hdaSelector, peekShouldBeArray ); +} + +// ------------------------------------------------------------------- ok state +spaceghost.then( function checkOkState(){ + this.test.comment( 'HDAs in the "ok" state should be well formed' ); + this.withFrame( this.selectors.frames.history, function(){ - var model = this.evaluate( function(){ - return Galaxy.currHistoryPanel.model.hdas.at( 0 ).attributes; + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + this.test.assertVisible( uploadSelector, 'HDA is visible' ); + + this.test.comment( 'should have the proper state class' ); + var okStateClass = this.historypanel.data.selectors.hda.wrapper.stateClasses.ok, + uploadElement = this.getElementInfo( uploadSelector ); + this.test.assert( uploadElement.attributes['class'].indexOf( okStateClass ) !== -1, + 'HDA has "ok" state class' ); + + // since we're using css there's no great way to test state icon (.state-icon is empty) + + this.test.comment( 'should have proper title and hid' ); + testTitle.call( spaceghost, uploadSelector, testUploadInfo.hid, testUploadInfo.name ); + + this.test.comment( 'should have all of the three, main buttons' ); + testTitleButtonStructure.call( spaceghost, uploadSelector ); + + this.test.comment( 'body is not visible before clicking the hda title' ); + var body = uploadSelector + ' ' + this.historypanel.data.selectors.hda.body; + this.test.assertNotVisible( body, 'body is not visible' ); + + this.test.comment( 'clicking the hda title should expand its body' ); + var hdaTitle = uploadSelector + ' ' + this.historypanel.data.selectors.hda.title; + this.click( hdaTitle ); + this.wait( 500, function(){ + testExpandedBody.call( spaceghost, uploadSelector, summaryShouldBeArray, infoShouldBe, false ); }); - this.info( 'model:' + this.jsonStr( model ) ); }); }); +/* spaceghost.then( function checkNewState(){ this.test.comment( 'HDAs in the "new" state should be well formed' ); @@ -93,15 +289,10 @@ this.test.assertVisible( uploadSelector, 'HDA is visible' ); // should have proper title and hid - var titleSelector = uploadSelector + ' .historyItemTitle'; - this.test.assertVisible( titleSelector, 'HDA title is visible' ); - this.test.assertSelectorHasText( titleSelector, testUploadInfo.name, - 'HDA has proper title' ); - this.test.assertSelectorHasText( titleSelector, testUploadInfo.hid, - 'HDA has proper hid' ); + testTitle.call( spaceghost, uploadSelector, testUploadInfo.hid, testUploadInfo.name ); // should have the new state class - var newStateClass = 'historyItem-new', + var newStateClass = this.historypanel.data.selectors.hda.wrapper.stateClasses['new']; uploadElement = this.getElementInfo( uploadSelector ); this.test.assert( uploadElement.attributes['class'].indexOf( newStateClass ) !== -1, 'HDA has new state class' ); @@ -111,13 +302,13 @@ //this.test.assertVisible( stateIconSelector, 'HDA has proper hid' ); // should NOT have any of the three, main buttons - var buttonSelector = uploadSelector + ' .historyItemButtons a'; + var buttonSelector = uploadSelector + ' ' + this.historypanel.data.selectors.hda.titleButtons + ' a'; this.test.assertDoesntExist( buttonSelector, 'No display, edit, or delete buttons' ); // expand and check the body this.click( titleSelector ); this.wait( 500, function(){ - var bodySelector = uploadSelector + ' .historyItemBody'; + var bodySelector = uploadSelector + ' ' + this.historypanel.data.selectors.hda.body; this.test.assertVisible( bodySelector, 'HDA body is visible (after expanding)' ); var expectedBodyText = 'This is a new dataset'; @@ -130,8 +321,7 @@ }); }); }); - -// =================================================================== TESTS +*/ // =================================================================== diff -r 4827bed265e7563c58346654f5a668f5de6d9d68 -r c2f310489973fc2ba42e5289ccec3ed88a42aabc test/casperjs/history-panel-tests.js --- a/test/casperjs/history-panel-tests.js +++ b/test/casperjs/history-panel-tests.js @@ -79,7 +79,7 @@ this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' ); }); -// grab the history frame bounds for mouse later tests +// grab the history frame bounds for later mouse tests spaceghost.then( function(){ historyFrameInfo = this.getElementInfo( 'iframe[name="galaxy_history"]' ); //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); diff -r 4827bed265e7563c58346654f5a668f5de6d9d68 -r c2f310489973fc2ba42e5289ccec3ed88a42aabc test/casperjs/modules/historypanel.js --- a/test/casperjs/modules/historypanel.js +++ b/test/casperjs/modules/historypanel.js @@ -136,12 +136,12 @@ }; //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. +/** Find the DOM 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 ){ +HistoryPanel.prototype.hdaElementIdByTitle = function hdaElementIdByTitle( title, hid ){ var elementInfo = this.hdaElementInfoByTitle( title, hid ); return (( elementInfo && elementInfo.attributes && elementInfo.attributes.id )? ( elementInfo.attributes.id ):( null )); @@ -200,6 +200,49 @@ return spaceghost; }; +/** Hover over an element in the history panel. + * This is re-implemented here because element bounds in iframes are calc'd + * relative to the iframe - but mouse coords are not. Capture the iframe + * bounds first to re-calc for mouse coords + * @param {String} selector a css or xpath selector for an historyItemWrapper + * @param {Function} whenHovering a function to call after the hover (will be scoped to spaceghost) + * @param {ElementInfo} historyFrameInfo casper ElementInfo for the history iframe (optional) + * If undefined, hoverOver will use withFrame first and gather the information itself. + * Send in history iframe info if you're already in the frame when calling this. bleh. + */ +HistoryPanel.prototype.hoverOver = function hoverOver( selector, whenHovering, historyFrameInfo ){ + var spaceghost = this.spaceghost; + + // helper function + function hoverAndCallback( historyFrameInfo, selector, whenHovering ){ + // ...this suddenly started working when I upped the viewport size + //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); + var elementInfo = this.getElementInfo( selector ), + newCoords = { x: ( historyFrameInfo.x + elementInfo.x ), + y: ( historyFrameInfo.y + elementInfo.y ) }; + //this.debug( 'elementInfo:' + this.jsonStr( elementInfo ) ); + //this.debug( 'newCoords:' + this.jsonStr( newCoords ) ); + this.page.sendEvent( 'mousemove', newCoords.x + 1, newCoords.y + 1 ); + if( whenHovering ){ + whenHovering.call( this ); + } + } + + // complicated by iframes + // if no history frame info was passed - assume not in history frame already and move into using withFrame + if( !historyFrameInfo ){ + //TODO: move selector to data (use selectors.frames? ) + historyFrameInfo = spaceghost.getElementInfo( 'iframe[name="galaxy_history"]' ); + spaceghost.withFrame( spaceghost.selectors.frames.history, function inHistoryPanel(){ + hoverAndCallback.call( spaceghost, historyFrameInfo, selector, whenHovering ); + }); + + // otherwise, assume we're already 'in' the history frame and use the passed info + } else { + hoverAndCallback.call( spaceghost, historyFrameInfo, selector, whenHovering ); + } + //return spaceghost; +}; // =================================================================== SELECTORS //TODO: data is not a very good name @@ -219,9 +262,22 @@ wrapper : { stateClasses : { prefix : 'historyItem-', - ok : 'historyItem-ok' + ok : 'historyItem-ok', + 'new' : 'historyItem-new' } - } + }, + title : '.historyItemTitle', + titleButtons : '.historyItemButtons', + displayButton : '.icon-button.display', + editAttrButton : '.icon-button.edit', + deleteButton : '.icon-button.delete', + body : '.historyItemBody', + summary : '.hda-summary', + dbkey : '.metadata-dbkey', + info : '.hda-info', + primaryActionButtons : 'div[id^="primary-actions"]', + secondaryActionButtons : 'div[id^="secondary-actions"]', + peek : 'pre.peek' } }, labels : { @@ -247,6 +303,23 @@ emptyMsg : "Your history is empty. Click 'Get Data' on the left pane to start" }, hda : { + ok: { + tooltips : { + displayButton : 'Display data in browser', + editAttrButton : 'Edit Attributes', + deleteButton : 'Delete' + }, + hrefs : { + displayButton : '/datasets/%s/display', + editAttrButton : '/datasets/%s/edit', + deleteButton : '/datasets/%s/delete_async' + }, + nodeNames : { + displayButton : 'a', + editAttrButton : 'a', + deleteButton : 'a' + } + } } } }; diff -r 4827bed265e7563c58346654f5a668f5de6d9d68 -r c2f310489973fc2ba42e5289ccec3ed88a42aabc test/casperjs/spaceghost.js --- a/test/casperjs/spaceghost.js +++ b/test/casperjs/spaceghost.js @@ -136,7 +136,6 @@ this.debug( 'clientScripts:\n' + this.jsonStr( this.options.clientScripts ) ); this._loadModules(); - }; /** Allow CLI arguments to set options if the proper option name is used. @@ -415,6 +414,15 @@ }; // =================================================================== PAGE CONTROL +/** An override of casper.start for additional set up. + * (Currently only used to change viewport) + */ +SpaceGhost.prototype.start = function start(){ + var returned = Casper.prototype.start.apply( this, arguments ); + this.viewport( 1024, 728 ); + return returned; +}; + /** An override of casper.open specifically for Galaxy. * (Currently only used to change language headers) */ @@ -492,18 +500,20 @@ }); }; -/** Override capture to save to environ: GALAXY_TEST_SAVE (or passed in from CLI) - * @param {String} filename the image filename +/** Hover over an element. + * NOTE: not for use with iframes (main, tool, history) - they need to re-calc + * for the iframe bounds and should be implemented in their own modules + * @param {String} selector a css or xpath selector for an historyItemWrapper + * @param {Function} whenHovering a function to call after the hover (will be scoped to spaceghost) */ -SpaceGhost.prototype.capture = function capture( filename, clipRect_or_selector ){ - //TODO: override with saved output dir - if( clipRect_or_selector && ( !utils.isClipRect( clipRect_or_selector ) ) ){ - this.debug( "USING CAPTURE SELECTOR" ); - return this.captureSelector( filename, clipRect_or_selector ); - } - return Casper.prototype.capture.apply( this, arguments ); +SpaceGhost.prototype.hoverOver = function hoverOver( selector, whenHovering ){ + var elementInfo = this.getElementInfo( selector ); + this.page.sendEvent( 'mousemove', elementInfo.x + 1, elementInfo.y + 1 ); + whenHovering.call( this ); + return this; }; + // =================================================================== TESTING //TODO: form fill doesn't work as casperjs would want it - often a button -> controller url //TODO: saveScreenshot (to GALAXY_TEST_SAVE) @@ -591,6 +601,15 @@ }); }; +/** Assert that a given string (toSearch) contains some given string (searchFor). + * @param {String} toSearch the string to search + * @param {String} searchFor the string to search for + * @param {String} msg assertion msg to display + */ +SpaceGhost.prototype.assertTextContains = function assertTextContains( toSearch, searchFor, msg ){ + this.test.assert( toSearch.indexOf( searchFor ) !== -1, msg ); +}; + // =================================================================== 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 @@ -619,6 +638,17 @@ // =================================================================== GALAXY CONVENIENCE // =================================================================== MISCELAIN +/** Override capture to save to environ: GALAXY_TEST_SAVE (or passed in from CLI) + * @param {String} filename the image filename + */ +SpaceGhost.prototype.capture = function capture( filename, clipRect_or_selector ){ + //TODO: override with saved output dir + if( clipRect_or_selector && ( !utils.isClipRect( clipRect_or_selector ) ) ){ + return this.captureSelector( filename, clipRect_or_selector ); + } + return Casper.prototype.capture.apply( this, arguments ); +}; + /** Pop all handlers for eventName from casper and return them in order. * @param {String} eventName the name of the event from which to remove handlers */ @@ -736,7 +766,7 @@ // =================================================================== TEST DATA // maintain selectors, labels, text here in one central location -//TODO: to separate file? +//TODO: to data SpaceGhost.prototype.selectors = { masthead : { userMenu : { 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