browser tests: cleanup, unify datasource format, add more documentation

This commit is contained in:
Carl Eberhard
2013-03-13 18:58:42 -04:00
parent 6d60adb2ad
commit 3aa6a5fdaa
7 changed files with 381 additions and 289 deletions
+18 -38
View File
@@ -39,12 +39,9 @@ if( spaceghost.fixtureData.testUser ){
spaceghost.info( 'Will use fixtureData.testUser: ' + email );
}
var tooltipSelector = '.bs-tooltip',
editableTextClass = 'editable-text',
editableTextInputSelector = 'input#renaming-active',
galaxyCookieName = 'galaxysession';
var tooltipSelector = spaceghost.data.selectors.tooltipBalloon,
editableTextClass = spaceghost.data.selectors.editableText,
editableTextInput = spaceghost.data.selectors.editableTextInput,
unnamedName = spaceghost.historypanel.data.text.history.newName,
nameSelector = spaceghost.historypanel.data.selectors.history.name,
@@ -57,6 +54,7 @@ var tooltipSelector = '.bs-tooltip',
anonNameTooltip = spaceghost.historypanel.data.text.anonymous.tooltips.name;
var historyFrameInfo = {},
filepathToUpload = '../../test-data/1.txt',
testUploadInfo = {};
@@ -69,18 +67,6 @@ spaceghost.thenOpen( spaceghost.baseUrl, function(){
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(){
@@ -90,7 +76,7 @@ spaceghost.then( function(){
spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){
this.test.comment( 'history panel for anonymous user, new history' );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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' );
@@ -118,33 +104,29 @@ spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){
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.historypanel.hoverOver( nameSelector, function testingHover(){
this.test.assertExists( tooltipSelector, "Found tooltip after name hover" );
this.test.assertSelectorHasText( tooltipSelector, anonNameTooltip );
}, historyFrameInfo );
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.assertDoesntHaveClass( nameSelector, editableTextClass,
"Name field is not classed as editable text" );
this.click( nameSelector );
this.test.assertDoesntExist( editableTextInputSelector, "Clicking on name does not create an input" );
this.test.assertDoesntExist( editableTextInput, "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 ){
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;
this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name );
uploadInfo = _uploadInfo;
testUploadInfo = _uploadInfo;
});
});
spaceghost.then( function testAnonUpload(){
@@ -157,18 +139,17 @@ spaceghost.then( function testAnonUpload(){
// ------------------------------------------------------------------- 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.withFrame( spaceghost.data.selectors.frames.history, function(){
var hdaInfo = this.historypanel.hdaElementInfoByTitle( testUploadInfo.name, testUploadInfo.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,
this.test.assert( testUploadInfo.hdaElement.attributes.id === hdaInfo.attributes.id,
"After logging in - found a matching hda by hda view id: " + hdaInfo.attributes.id );
}
});
@@ -177,8 +158,7 @@ spaceghost.thenOpen( spaceghost.baseUrl, function(){
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.withFrame( spaceghost.data.selectors.frames.history, function(){
this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName );
this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr,
'Message contains "' + emptyMsgStr + '"' );
+45 -23
View File
@@ -40,7 +40,7 @@ if( spaceghost.fixtureData.testUser ){
spaceghost.info( 'Will use fixtureData.testUser: ' + email );
}
var tooltipSelector = '.bs-tooltip';
var tooltipSelector = spaceghost.data.selectors.tooltipBalloon;
var utils = require( 'utils' ),
historyFrameInfo = {},
@@ -70,8 +70,7 @@ spaceghost.then( function upload(){
});
// =================================================================== TESTS
// ------------------------------------------------------------------- helpers
// =================================================================== TEST HELPERS
//NOTE: to be called with fn.call( spaceghost, ... )
function testTitle( hdaSelector, hid, name ){
@@ -238,19 +237,18 @@ function testExpandedBody( hdaSelector, expectedSummaryTextArray, expectedInfoTe
testPeek.call( this, hdaSelector, peekShouldBeArray );
}
// =================================================================== TESTS
// ------------------------------------------------------------------- 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(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
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' );
this.assertHasClass( uploadSelector, this.historypanel.data.selectors.hda.wrapper.stateClasses.ok,
'HDA has ok state class' );
// since we're using css there's no great way to test state icon (.state-icon is empty)
@@ -273,11 +271,27 @@ spaceghost.then( function checkOkState(){
});
});
/*
// restore to collapsed
spaceghost.then( function collapseOkState(){
this.test.comment( "Collapsing hda in 'ok' state should hide body again" );
this.withFrame( spaceghost.data.selectors.frames.history, function(){
var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id,
hdaTitle = uploadSelector + ' ' + this.historypanel.data.selectors.hda.title;
body = uploadSelector + ' ' + this.historypanel.data.selectors.hda.body;
this.click( hdaTitle );
this.wait( 500, function(){
this.test.assertNotVisible( body, 'body is not visible' );
});
});
});
// ------------------------------------------------------------------- new state
spaceghost.then( function checkNewState(){
this.test.comment( 'HDAs in the "new" state should be well formed' );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
// set state directly through model
//TODO: not ideal
this.evaluate( function(){
@@ -291,37 +305,45 @@ spaceghost.then( function checkNewState(){
// should have proper title and hid
testTitle.call( spaceghost, uploadSelector, testUploadInfo.hid, testUploadInfo.name );
// should have the new state class
var newStateClass = this.historypanel.data.selectors.hda.wrapper.stateClasses['new'];
uploadElement = this.getElementInfo( uploadSelector );
this.test.assert( uploadElement.attributes['class'].indexOf( newStateClass ) !== -1,
this.test.comment( 'new HDA should have the new state class' );
this.assertHasClass( uploadSelector, this.historypanel.data.selectors.hda.wrapper.stateClasses['new'],
'HDA has new state class' );
// since we're using css there's no great way to test this
//var stateIconSelector = uploadSelector + ' .state-icon';
//this.test.assertVisible( stateIconSelector, 'HDA has proper hid' );
// should NOT have any of the three, main buttons
this.test.comment( 'new HDA should NOT have any of the three, main buttons' );
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.test.comment( 'clicking the title of the new HDA will expand the body' );
var hdaTitle = uploadSelector + ' ' + this.historypanel.data.selectors.hda.title;
this.click( hdaTitle );
this.wait( 500, function(){
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';
this.test.comment( 'the body should have the text: ' + expectedBodyText );
this.test.assertSelectorHasText( bodySelector, expectedBodyText,
'HDA body has text: ' + expectedBodyText );
// restore to collapsed
this.click( titleSelector );
this.click( hdaTitle );
});
});
});
});
*/
// restore state, collapse
spaceghost.then( function revertStateAndCollapse(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.evaluate( function(){
return Galaxy.currHistoryPanel.model.hdas.at( 0 ).set( 'state', 'ok' );
});
this.wait( 500, function(){
var hdaTitle = '#' + testUploadInfo.hdaElement.attributes.id
+ ' ' + this.historypanel.data.selectors.hda.title;
this.click( hdaTitle );
});
});
});
// ===================================================================
+62 -68
View File
@@ -38,7 +38,11 @@ if( spaceghost.fixtureData.testUser ){
}
// selectors and labels
var nameSelector = spaceghost.historypanel.data.selectors.history.name,
var tooltipSelector = spaceghost.data.selectors.tooltipBalloon,
editableTextClass = spaceghost.data.selectors.editableText,
editableTextInput = spaceghost.data.selectors.editableTextInput,
nameSelector = spaceghost.historypanel.data.selectors.history.name,
subtitleSelector = spaceghost.historypanel.data.selectors.history.subtitle,
unnamedName = spaceghost.historypanel.data.text.history.newName,
initialSizeStr = spaceghost.historypanel.data.text.history.newSize,
@@ -51,14 +55,9 @@ var nameSelector = spaceghost.historypanel.data.selectors.history.name,
annoAreaSelector = spaceghost.historypanel.data.selectors.history.annoArea,
nameTooltip = spaceghost.historypanel.data.text.history.tooltips.name,
tooltipSelector = '.bs-tooltip',
editableTextClass = 'editable-text',
editableTextInputSelector = 'input#renaming-active',
refreshButtonSelector = 'a#history-refresh-button',
refreshButtonIconSelector = 'span.fa-icon-refresh',
refreshButtonHref = '/history',
refreshButtonSelector = 'a#history-refresh-button',
refreshButtonIconSelector = 'span.fa-icon-refresh',
refreshButtonHref = '/history',
includeDeletedOptionsLabel = spaceghost.historyoptions.data.labels.options.includeDeleted;
@@ -66,7 +65,7 @@ var nameSelector = spaceghost.historypanel.data.selectors.history.name,
var newHistoryName = "Test History",
filepathToUpload = '../../test-data/1.txt',
historyFrameInfo = {},
uploadInfo = {};
testUploadInfo = {};
// =================================================================== TESTS
@@ -88,7 +87,7 @@ spaceghost.then( function(){
// ------------------------------------------------------------------- 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(){
this.withFrame( spaceghost.data.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' );
@@ -98,7 +97,7 @@ spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){
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.comment( "history subtitle should display size and size should be: " + initialSizeStr );
this.test.assertExists( subtitleSelector, 'Found ' + subtitleSelector );
this.test.assertVisible( subtitleSelector, 'History subtitle is visible' );
this.test.assertSelectorHasText( subtitleSelector, initialSizeStr,
@@ -119,50 +118,46 @@ spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){
// ------------------------------------------------------------------- name editing
spaceghost.then( function(){
this.test.comment( 'history panel, editing the history name' );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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.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.assertHasClass( nameSelector, editableTextClass, "Name field classed for editable text" );
this.click( nameSelector );
this.test.assertExists( editableTextInputSelector, "Clicking on name creates an input" );
this.test.assertExists( editableTextInput, "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" );
this.test.assertDoesntExist( editableTextInput, "Input disappears after pressing enter" );
});
});
});
spaceghost.then( function(){
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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 );
var inputInfo = this.getElementInfo( editableTextInput );
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" );
this.test.assertDoesntExist( editableTextInput, "Input disappears after clicking away" );
});
});
});
spaceghost.then( function(){
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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" );
@@ -170,7 +165,7 @@ spaceghost.then( function(){
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" );
this.test.assertDoesntExist( editableTextInput, "Input disappears after hitting ESC" );
});
});
});
@@ -185,14 +180,14 @@ spaceghost.then( function upload(){
hasClass = _uploadInfo.hdaElement.attributes[ 'class' ],
hasOkClass = _uploadInfo.hdaElement.attributes[ 'class' ].indexOf( wrapperOkClassName ) !== -1;
this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name );
uploadInfo = _uploadInfo;
testUploadInfo = _uploadInfo;
});
});
spaceghost.then( function checkPanelStructure(){
this.test.comment( 'checking structure of non-empty panel' );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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' );
@@ -221,8 +216,7 @@ spaceghost.then( function checkPanelStructure(){
//TODO: check tooltips
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.capture( 'tag-area.png' );
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.mouseEvent( 'click', tagIconSelector );
this.wait( 1000, function(){
this.test.assertVisible( tagAreaSelector, 'Tag area is now displayed' );
@@ -235,7 +229,7 @@ spaceghost.then( function openTags(){
//TODO: check tooltips
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.withFrame( spaceghost.data.selectors.frames.history, function(){
this.mouseEvent( 'click', annoIconSelector );
this.wait( 1000, function(){
this.test.assertVisible( annoAreaSelector, 'Annotation area is now displayed' );
@@ -244,7 +238,7 @@ spaceghost.then( function openAnnotation(){
});
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.withFrame( spaceghost.data.selectors.frames.history, function bler(){
this.mouseEvent( 'click', annoIconSelector );
this.wait( 1000, function(){
this.test.assertNotVisible( annoAreaSelector, 'Tag area is now hidden' );
@@ -296,8 +290,9 @@ spaceghost.then( function historyOptions(){
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" );
this.historypanel.deleteHda( '#' + testUploadInfo.hdaElement.attributes.id, function(){
this.test.assertDoesntExist( '#' + testUploadInfo.hdaElement.attributes.id,
"Deleted HDA is not in the DOM" );
});
});
@@ -306,11 +301,11 @@ spaceghost.then( function(){
this.test.comment( 'History options->' + includeDeletedOptionsLabel + ' shows deleted datasets' );
this.historyoptions.includeDeleted();
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.waitForSelector( nameSelector, function(){
this.test.assertExists( '#' + uploadInfo.hdaElement.attributes.id,
this.test.assertExists( '#' + testUploadInfo.hdaElement.attributes.id,
"Deleted HDA is in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" );
this.test.assertVisible( '#' + uploadInfo.hdaElement.attributes.id,
this.test.assertVisible( '#' + testUploadInfo.hdaElement.attributes.id,
"Deleted HDA is visible again (using history options -> " + includeDeletedOptionsLabel + ")" );
});
});
@@ -320,9 +315,9 @@ 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.withFrame( spaceghost.data.selectors.frames.history, function(){
this.waitForSelector( nameSelector, function(){
this.test.assertDoesntExist( '#' + uploadInfo.hdaElement.attributes.id,
this.test.assertDoesntExist( '#' + testUploadInfo.hdaElement.attributes.id,
"Deleted HDA is not in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" );
});
});
@@ -331,10 +326,10 @@ spaceghost.then( function(){
// undelete the uploaded file
spaceghost.then( function(){
this.historyoptions.includeDeleted();
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.waitForSelector( nameSelector, function(){
//TODO: to conv. fn
this.click( '#' + uploadInfo.hdaElement.attributes.id
this.click( '#' + testUploadInfo.hdaElement.attributes.id
+ ' ' + this.historypanel.data.selectors.history.undeleteLink );
});
});
@@ -348,16 +343,13 @@ spaceghost.then( function(){
// 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;
var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id;
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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" );
this.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body,
"Body for uploaded file is visible" );
});
});
});
@@ -365,13 +357,13 @@ spaceghost.then( function(){
// ------------------------------------------------------------------- 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;
var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id;
this.click( refreshButtonSelector );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.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.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body,
"Body for uploaded file is visible" );
});
});
// this will break: webkit + jq 1.7
@@ -380,26 +372,27 @@ spaceghost.then( function(){
// ------------------------------------------------------------------- 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' );
var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id;
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.click( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.title );
this.wait( 500, function(){
this.test.assertNotVisible( uploadedSelector + ' .hda-summary', "hda-summary is not visible" );
this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body,
"Body for uploaded file is not visible" );
});
});
});
// ------------------------------------------------------------------- collapsed hdas are still collapsed after a refresh
// ------------------------------------------------------------------- collapsed hdas 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.test.comment( 'collapsed hdas still collapsed after a refresh' );
var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id;
this.click( refreshButtonSelector );
this.withFrame( this.selectors.frames.history, function(){
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.waitForSelector( nameSelector, function(){
this.test.assertNotVisible( uploadedSelector + ' .hda-summary', "hda-summary is not visible" );
this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body,
"Body for uploaded file is not visible" );
});
});
});
@@ -407,19 +400,20 @@ spaceghost.then( function(){
// ------------------------------------------------------------------- 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.withFrame( spaceghost.data.selectors.frames.history, function(){
this.click( '#' + testUploadInfo.hdaElement.attributes.id + ' ' + this.historypanel.data.selectors.hda.title );
this.wait( 500, function(){});
});
});
spaceghost.then( function(){
this.test.comment( 'History option collapses all expanded hdas' );
var uploadedSelector = '#' + uploadInfo.hdaElement.attributes.id;
var uploadedSelector = '#' + testUploadInfo.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" );
this.withFrame( spaceghost.data.selectors.frames.history, function(){
this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body,
"Body for uploaded file is not visible" );
});
});
});
+4 -4
View File
@@ -73,7 +73,7 @@ HistoryPanel.prototype.waitForHdaState = function waitForHdaState( hdaSelector,
finalStateClass = '.historyItem-' + finalState;
spaceghost.then( function(){
spaceghost.withFrame( spaceghost.selectors.frames.history, function(){
spaceghost.withFrame( spaceghost.data.selectors.frames.history, function(){
// save the old time out
var oldWaitTimeout = spaceghost.options.waitTimeout,
@@ -157,7 +157,7 @@ HistoryPanel.prototype.deleteHda = function deleteHda( hdaSelector, whenDeletedF
whenDeletedFn = whenDeletedFn || function(){};
var spaceghost = this.spaceghost;
spaceghost.withFrame( spaceghost.selectors.frames.history, function deletingHda(){
spaceghost.withFrame( spaceghost.data.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;
@@ -192,7 +192,7 @@ 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(){
spaceghost.withFrame( spaceghost.data.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 );
@@ -233,7 +233,7 @@ HistoryPanel.prototype.hoverOver = function hoverOver( selector, whenHovering, h
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(){
spaceghost.withFrame( spaceghost.data.selectors.frames.history, function inHistoryPanel(){
hoverAndCallback.call( spaceghost, historyFrameInfo, selector, whenHovering );
});
+15 -29
View File
@@ -22,7 +22,11 @@ Tools.prototype.toString = function toString(){
return this.spaceghost + '.Tools';
};
// -------------------------------------------------------------------
/* TODO:
move selectors from sg to here
*/
// =================================================================== INTERNAL
/** Tests uploading a file.
* NOTE: this version does NOT throw an error on a bad upload.
@@ -35,52 +39,34 @@ Tools.prototype._uploadFile = function _uploadFile( filepath ){
uploadInfo = {};
//TODO: check file exists using phantom.fs
//TODO: pull from test data
uploadInfo[ spaceghost.selectors.tools.upload.fileInput ] = filepath;
uploadInfo[ spaceghost.data.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.withFrame( spaceghost.data.selectors.frames.tools, function(){
spaceghost.clickLabel( spaceghost.data.labels.tools.upload.panelLabel );
});
});
spaceghost.then( function beginUpload(){
spaceghost.withFrame( spaceghost.selectors.frames.main, function(){
spaceghost.fill( spaceghost.selectors.tools.general.form, uploadInfo, false );
spaceghost.withFrame( spaceghost.data.selectors.frames.main, function(){
spaceghost.fill( spaceghost.data.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 ) );
spaceghost.click( xpath( spaceghost.data.selectors.tools.general.executeButton_xpath ) );
});
});
// debugging
spaceghost.withFrame( spaceghost.selectors.frames.main, function afterUpload(){
var messageInfo = spaceghost.elementInfoOrNull( spaceghost.selectors.messages.all );
spaceghost.withFrame( spaceghost.data.selectors.frames.main, function afterUpload(){
var messageInfo = spaceghost.elementInfoOrNull( spaceghost.data.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
*/
@@ -159,9 +145,9 @@ Tools.prototype.uploadFile = function uploadFile( filepath, callback, timeoutAft
// 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.withFrame( spaceghost.data.selectors.frames.main, function toolExecuted(){
spaceghost.debug( 'checking for done message' );
var doneElementInfo = spaceghost.elementInfoOrNull( spaceghost.selectors.messages.donelarge );
var doneElementInfo = spaceghost.elementInfoOrNull( spaceghost.data.selectors.messages.donelarge );
if( !doneElementInfo ){
throw new spaceghost.GalaxyError( 'Upload Error: no done message uploading "' + filepath + '"' );
}
@@ -178,7 +164,7 @@ Tools.prototype.uploadFile = function uploadFile( filepath, callback, timeoutAft
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(){
spaceghost.withFrame( spaceghost.data.selectors.frames.history, function(){
spaceghost.waitForSelector( '#history-name', function(){
var hdaInfo = spaceghost.historypanel.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid );
if( hdaInfo === null ){
+24 -23
View File
@@ -43,19 +43,19 @@ User.prototype._submitRegistration = function _submitRegistration( email, passwo
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.clickLabel( spaceghost.data.labels.masthead.menus.user );
spaceghost.clickLabel( spaceghost.data.labels.masthead.userMenu.register );
spaceghost.withFrame( spaceghost.selectors.frames.main, function mainBeforeRegister(){
spaceghost.withFrame( spaceghost.data.selectors.frames.main, function mainBeforeRegister(){
spaceghost.debug( 'submitting registration... ' + spaceghost.getCurrentUrl() );
spaceghost.fill( spaceghost.selectors.registrationPage.form, userInfo, false );
spaceghost.fill( spaceghost.data.selectors.registrationPage.form, userInfo, false );
// need manual submit (not a normal html form)
spaceghost.click( xpath( spaceghost.selectors.registrationPage.submit_xpath ) );
spaceghost.click( xpath( spaceghost.data.selectors.registrationPage.submit_xpath ) );
});
//// debugging
//spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterRegister(){
// var messageInfo = spaceghost.getElementInfo( spaceghost.selectors.messages.all );
//spaceghost.withFrame( spaceghost.data.selectors.frames.main, function mainAfterRegister(){
// var messageInfo = spaceghost.getElementInfo( spaceghost.data.selectors.messages.all );
// spaceghost.debug( 'post registration message:\n' + spaceghost.jsonStr( messageInfo ) );
//});
});
@@ -78,21 +78,21 @@ User.prototype._submitLogin = function _submitLogin( email, password ){
spaceghost.thenOpen( spaceghost.baseUrl, function(){
spaceghost.clickLabel( spaceghost.labels.masthead.menus.user );
spaceghost.clickLabel( spaceghost.labels.masthead.userMenu.login );
spaceghost.clickLabel( spaceghost.data.labels.masthead.menus.user );
spaceghost.clickLabel( spaceghost.data.labels.masthead.userMenu.login );
spaceghost.withFrame( spaceghost.selectors.frames.main, function mainBeforeLogin(){
spaceghost.withFrame( spaceghost.data.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 ) );
spaceghost.fill( spaceghost.data.selectors.loginPage.form, loginInfo, false );
spaceghost.click( xpath( spaceghost.data.selectors.loginPage.submit_xpath ) );
});
//// debugging
//spaceghost.withFrame( spaceghost.selectors.frames.main, function mainAfterLogin(){
//spaceghost.withFrame( spaceghost.data.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 );
// if( spaceghost.getCurrentUrl().search( spaceghost.data.selectors.loginPage.url_regex ) != -1 ){
// var messageInfo = spaceghost.getElementInfo( spaceghost.data.selectors.messages.all );
// spaceghost.debug( 'post login message:\n' + spaceghost.jsonStr( messageInfo ) );
// }
//});
@@ -111,8 +111,8 @@ 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.withFrame( spaceghost.data.selectors.frames.main, function mainAfterRegister(){
var messageInfo = spaceghost.getElementInfo( spaceghost.data.selectors.messages.all );
spaceghost.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) );
if( messageInfo.attributes[ 'class' ] === 'errormessage' ){
@@ -132,9 +132,9 @@ 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 );
spaceghost.withFrame( spaceghost.data.selectors.frames.main, function mainAfterLogin(){
if( spaceghost.getCurrentUrl().search( spaceghost.data.selectors.loginPage.url_regex ) != -1 ){
var messageInfo = spaceghost.getElementInfo( spaceghost.data.selectors.messages.all );
if( messageInfo && messageInfo.attributes[ 'class' ] === 'errormessage' ){
throw new spaceghost.GalaxyError( 'LoginError: ' + messageInfo.html );
}
@@ -154,7 +154,8 @@ User.prototype.loggedInAs = function loggedInAs(){
var spaceghost = this.spaceghost,
userEmail = '';
try {
var loggedInInfo = spaceghost.getElementInfo( xpath( spaceghost.selectors.masthead.userMenu.userEmail_xpath ) );
var loggedInInfo = spaceghost.getElementInfo(
xpath( spaceghost.data.selectors.masthead.userMenu.userEmail_xpath ) );
userEmail = loggedInInfo.text;
} catch( err ){
spaceghost.error( err );
@@ -170,8 +171,8 @@ User.prototype.logout = function logout(){
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 );
spaceghost.clickLabel( spaceghost.data.labels.masthead.menus.user );
spaceghost.clickLabel( spaceghost.data.labels.masthead.userMenu.logout );
});
return spaceghost;
};
+213 -104
View File
@@ -64,11 +64,13 @@ var Casper = require( 'casper' ).Casper;
var utils = require( 'utils' );
// ------------------------------------------------------------------- inheritance
/**
/** @class An extension of the Casper object with methods and overrides specifically
* for interacting with a Galaxy web page.
* @augments Casper
*/
function SpaceGhost(){
SpaceGhost.super_.apply( this, arguments );
this.init.apply( this, arguments );
this._init.apply( this, arguments );
}
utils.inherits( SpaceGhost, Casper );
@@ -85,6 +87,9 @@ utils.inherits( SpaceGhost, Casper );
// ------------------------------------------------------------------- error types
PageError.prototype = new CasperError();
PageError.prototype.constructor = CasperError;
/** @class Represents a javascript error on the page casper is browsing
* (as opposed to an error in the test script).
*/
function PageError(){
CasperError.apply( this, arguments );
this.name = "PageError";
@@ -93,6 +98,7 @@ SpaceGhost.prototype.PageError = PageError;
GalaxyError.prototype = new CasperError();
GalaxyError.prototype.constructor = CasperError;
/** @class Thrown when Galaxy has (gracefully?) indicated pilot error. */
function GalaxyError(){
CasperError.apply( this, arguments );
this.name = "GalaxyError";
@@ -101,6 +107,7 @@ SpaceGhost.prototype.GalaxyError = GalaxyError;
AlertError.prototype = new CasperError();
AlertError.prototype.constructor = CasperError;
/** @class Thrown when Galaxy has displayed a javascript alert. */
function AlertError(){
CasperError.apply( this, arguments );
this.name = "AlertError";
@@ -111,14 +118,17 @@ SpaceGhost.prototype.AlertError = AlertError;
// ------------------------------------------------------------------- set up
/** More initialization: cli, event handlers, etc.
* @param {Object} options option hash
* @private
*/
SpaceGhost.prototype.init = function init( options ){
SpaceGhost.prototype._init = function _init( options ){
//console.debug( 'init, options:', JSON.stringify( options, null, 2 ) );
//NOTE: cli will override in-script options
this._setOptionsFromCli();
// save errors for later output (needs to go before process CLI)
/** cache of errors that have occurred
* @memberOf SpaceGhost */
this.errors = [];
this.on( 'error', function pushErrorToStack( msg, backtrace ){
//this.debug( 'adding error to stack: ' + msg + ', trace:' + JSON.stringify( backtrace, null, 2 ) );
@@ -139,8 +149,9 @@ SpaceGhost.prototype.init = function init( options ){
};
/** Allow CLI arguments to set options if the proper option name is used.
* @example:
* @example
* casperjs myscript.js --verbose=true --logLevel=debug
* @private
*/
SpaceGhost.prototype._setOptionsFromCli = function setOptionsFromCli(){
// get and remove any casper options passed on the command line
@@ -154,25 +165,8 @@ SpaceGhost.prototype._setOptionsFromCli = function setOptionsFromCli(){
};
// ------------------------------------------------------------------- cli args and options
SpaceGhost.prototype._saveHtmlOnErrorHandler = function _saveHtmlOnErrorHandler( msg, backtrace ){
// needs to output to a file in GALAXY_SAVE
//this.debugHTML();
};
SpaceGhost.prototype._saveTextOnErrorHandler = function _saveTextOnErrorHandler( msg, backtrace ){
// needs to output to a file in GALAXY_SAVE
//this.debugPage();
};
SpaceGhost.prototype._saveScreenOnErrorHandler = function _saveScreenOnErrorHandler( msg, backtrace ){
// needs to output to a pic in GALAXY_SAVE
//var filename = ...??
//?? this.getCurrentUrl(), this.getCurrent
//this.capture( filename );
};
/** Set up any SG specific options passed in on the cli.
* @private
*/
SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){
//this.debug( 'cli: ' + this.jsonStr( this.cli ) );
@@ -262,6 +256,7 @@ SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){
};
/** Suppress the normal output from the casper object (echo, errors)
* @private
*/
SpaceGhost.prototype._suppressOutput = function _suppressOutput(){
// currently (1.0) the only way to suppress test pass/fail messages
@@ -274,6 +269,7 @@ SpaceGhost.prototype._suppressOutput = function _suppressOutput(){
};
/** Suppress the normal output from the casper object (echo, errors)
* @private
*/
SpaceGhost.prototype._redirectOutputToStderr = function _redirectOutputToStderr(){
// currently (1.0) the only way to suppress test pass/fail messages
@@ -288,7 +284,7 @@ SpaceGhost.prototype._redirectOutputToStderr = function _redirectOutputToStderr(
this.removeListener( 'error', this.listeners( 'error' )[1] );
};
/** Outputs logs, test results and errors in a single JSON formatted object.
/** Outputs logs, test results and errors in a single JSON formatted object to the console.
*/
SpaceGhost.prototype.outputStateAsJson = function outputStateAsJson(){
var returnedJSON = {
@@ -303,18 +299,20 @@ SpaceGhost.prototype.outputStateAsJson = function outputStateAsJson(){
// ------------------------------------------------------------------- event handling
//note: using non-anon fns to allow removal if needed
// most of these are stubs (w logging) for later expansion
/** Event handler for failed page loads
/** 'load failed' Event handler for failed page loads that only records to the log
* @private
*/
SpaceGhost.prototype._loadFailedHandler = function _loadFailedHandler( object ){
this.error( 'load.failed: ' + spaceghost.jsonStr( object ) );
//TODO: throw error?
};
/** Event handler for page errors (js) - throws test scope as PageError
/** 'page.error' Event handler that re-raises as PageError
* NOTE: this has some special handling for DOM exc 12 which some casper selectors are throwing
* (even tho the selector still works)
* @throws {PageError} (with original's msg and backtrace)
* @private
*/
SpaceGhost.prototype._pageErrorHandler = function _pageErrorHandler( msg, backtrace ){
// add a page error handler to catch page errors (what we're most interested with here)
@@ -337,7 +335,9 @@ SpaceGhost.prototype._pageErrorHandler = function _pageErrorHandler( msg, backtr
}
};
/** Event handler for step/casper timeouts - throws PageError
/** 'timeout' Event handler for step/casper timeouts - raises as PageError
* @throws {PageError} Timeout occurred
* @private
*/
SpaceGhost.prototype._timeoutHandler = function _timeoutHandler(){
console.debug( 'timeout' );
@@ -345,17 +345,13 @@ SpaceGhost.prototype._timeoutHandler = function _timeoutHandler(){
throw new PageError( 'Timeout occurred' );
};
/** Event handler for console messages from the page.
*/
SpaceGhost.prototype._pageConsoleHandler = function _pageConsoleHandler(){
// remote.message
var DELIM = '-';
this.debug( this + '(page console) "' + Array.prototype.join.call( arguments, DELIM ) + '"' );
};
/** Event handler for alerts
/** 'alert' Event handler that raises an AlertError with the alert message
* @throws {AlertError} (the alert message)
* @private
*/
SpaceGhost.prototype._alertHandler = function _alertHandler( message ){
//TODO: this still isn't working well...
// casper info level already has outputs these
//this.warning( this + '(page alert)\n"' + message + '"' );
var ALERT_MARKER = '(page alert) ';
@@ -374,13 +370,34 @@ SpaceGhost.prototype._alertHandler = function _alertHandler( message ){
}
};
/** Event handler for navigation requested (loading of frames, redirects(?))
/** 'error' Event handler that saves html from the errored page.
* @private
*/
SpaceGhost.prototype._navHandler = function _navHandler( url, navigationType, navigationLocked, isMainFrame ){
this.debug( 'navigation.requested: ' + url );
SpaceGhost.prototype._saveHtmlOnErrorHandler = function _saveHtmlOnErrorHandler( msg, backtrace ){
// needs to output to a file in GALAXY_SAVE
//this.debugHTML();
};
/** Set up event handlers.
/** 'error' Event handler that saves text from the errored page.
* @private
*/
SpaceGhost.prototype._saveTextOnErrorHandler = function _saveTextOnErrorHandler( msg, backtrace ){
// needs to output to a file in GALAXY_SAVE
//this.debugPage();
};
/** 'error' Event handler that saves a screenshot of the errored page.
* @private
*/
SpaceGhost.prototype._saveScreenOnErrorHandler = function _saveScreenOnErrorHandler( msg, backtrace ){
// needs to output to a pic in GALAXY_SAVE
//var filename = ...??
//?? this.getCurrentUrl(), this.getCurrent
//this.capture( filename );
};
/** Sets up event handlers.
* @private
*/
SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){
//console.debug( '_setUpEventHandlers' );
@@ -393,18 +410,17 @@ SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){
this.on( 'waitFor.timeout', this._timeoutHandler );
// ........................ page info/debugging
// these are already displayed at the casper info level
//this.on( 'remote.message', this._pageConsoleHandler );
this.on( 'remote.alert', this._alertHandler );
// these are already displayed at the casper debug level
//this.on( 'navigation.requested', this._navHandler );
};
// ------------------------------------------------------------------- sub modules
/** Load sub modules (similar to casperjs.test)
* @requires User modules/user.js
* @requires Tools modules/tools.js
* @requires HistoryPanel modules/historypanel.js
* @requires HistoryOptions modules/historyoptions.js
* @private
*/
SpaceGhost.prototype._loadModules = function _loadModules(){
this.user = require( this.options.scriptDir + 'modules/user' ).create( this );
@@ -416,6 +432,7 @@ SpaceGhost.prototype._loadModules = function _loadModules(){
// =================================================================== PAGE CONTROL
/** An override of casper.start for additional set up.
* (Currently only used to change viewport)
* @see Casper#start
*/
SpaceGhost.prototype.start = function start(){
var returned = Casper.prototype.start.apply( this, arguments );
@@ -423,8 +440,9 @@ SpaceGhost.prototype.start = function start(){
return returned;
};
/** An override of casper.open specifically for Galaxy.
/** An override of casper.open for additional page control.
* (Currently only used to change language headers)
* @see Casper#open
*/
SpaceGhost.prototype.open = function open(){
//TODO: this can be moved to start (I think...?)
@@ -433,13 +451,13 @@ SpaceGhost.prototype.open = function open(){
return Casper.prototype.open.apply( this, arguments );
};
/** An override to provide json output and more informative error codes
/** An override to provide json output and more informative error codes.
* Exits with 2 if a test has failed.
* Exits with 1 if some error has occurred.
* Exits with 0 if all tests passed.
* @see Casper#run run, boy, run (doesn't he fly?)
*/
SpaceGhost.prototype.run = function run( onComplete, time ){
// wrap the onComplete to:
// return code 2 on test failure
// 0 on success
// (1 on js error - in error handler)
var new_onComplete = function(){
onComplete.call( this );
var returnCode = ( this.test.testResults.failed )?( 2 ):( 0 );
@@ -467,7 +485,6 @@ SpaceGhost.prototype.run = function run( onComplete, time ){
* @param {Function} catchFn some portion of the correct error msg
*/
SpaceGhost.prototype.tryStepsCatch = function tryStepsCatch( stepsFn, catchFn ){
//TODO: * @param {Boolean} removeOtherListeners option to remove other listeners while this fires
// create three steps: 1) set up new error handler, 2) try the fn, 3) check for errors and rem. handler
var originalExitOnError,
originalErrorHandlers = [],
@@ -522,11 +539,10 @@ SpaceGhost.prototype.hoverOver = function hoverOver( selector, whenHovering ){
/** Casper has an (undocumented?) skip test feature. This is a conv. wrapper for that.
*/
SpaceGhost.prototype.skipTest = function(){
//TODO: does this work? seems to...
throw this.test.SKIP_MESSAGE;
};
/** test helper - within frame, assert selector, and assert text in selector
/** Test helper - within frame, assert selector, and assert text in selector
* @param {CasperJS selector} selector what element in which to search for the text
* @param {String} text what text to search for
* @param {String} frame frame selector (gen. name) in which to search for selector (defaults to top)
@@ -548,15 +564,17 @@ SpaceGhost.prototype.assertSelectorAndTextInFrame = function assertSelectorAndTe
}
}
/** test helper - within frame, assert errormessage, and assert text in errormessage
/** Test helper - within frame, assert errormessage, and assert text in errormessage
* *message is a common UI feedback motif in Galaxy (often displayed in the main panel)
* @param {String} message what the message should contain
* @param {String} frame frame selector (gen. name) in which to search for selector (defaults to 'galaxy_main')
* @param {CasperJS selector} messageSelector what element in which to search for the text (defaults to '.errormessage')
* @param {String} message what the message should contain
* @param {String} frame frame selector (gen. name) in which to search for selector
* (defaults to 'galaxy_main')
* @param {CasperJS selector} messageSelector what element in which to search for the text
* (defaults to '.errormessage')
*/
SpaceGhost.prototype.assertErrorMessage = function assertSelectorAndTextInFrame( message, frame, messageSelector ){
messageSelector = messageSelector || this.selectors.messages.error;
frame = frame || this.selectors.frames.main;
messageSelector = messageSelector || this.data.selectors.messages.error;
frame = frame || this.data.selectors.frames.main;
this.assertSelectorAndTextInFrame( messageSelector, message, frame );
};
@@ -610,9 +628,32 @@ SpaceGhost.prototype.assertTextContains = function assertTextContains( toSearch,
this.test.assert( toSearch.indexOf( searchFor ) !== -1, msg );
};
/** Assert that a given element has a given class.
* @param {CasperJS selector} selector what element to test
* @param {String} className the class to test for (classes passed in with a leading '.' will have it trimmed)
*/
SpaceGhost.prototype.assertHasClass = function assertHasClass( selector, className, msg ){
className = ( className[0] == '.' )?( className.slice( 1 ) ):( className );
msg = msg || 'selector "' + selector + '" has class: "' + className + '"';
var classes = this.getElementAttribute( selector, 'class' );
this.test.assert( classes.indexOf( className ) !== -1, msg );
};
/** Assert that a given element doesn't have a given class.
* @param {CasperJS selector} selector what element to test
* @param {String} className the class to test for (classes passed in with a leading '.' will have it trimmed)
*/
SpaceGhost.prototype.assertDoesntHaveClass = function assertDoesntHaveClass( selector, className, msg ){
className = ( className[0] == '.' )?( className.slice( 1 ) ):( className );
msg = msg || 'selector "' + selector + '" has class: "' + className + '"';
var classes = this.getElementAttribute( selector, 'class' );
this.test.assert( classes.indexOf( className ) === -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
* @returns {Object|null} element info if found, null if not
*/
SpaceGhost.prototype.elementInfoOrNull = function elementInfoOrNull( selector ){
var found = null;
@@ -622,8 +663,9 @@ SpaceGhost.prototype.elementInfoOrNull = function elementInfoOrNull( selector ){
return found;
};
/** Wraps casper.click in try, returning true if element found and clicked, false if not instead of erroring.
/** Wraps casper.click in try to prevent error if element isn't found
* @param {String} selector css or xpath selector for the element to find
* @returns {Boolean} true if element found and clicked, false if not instead of erroring
*/
SpaceGhost.prototype.tryClick = function tryClick( selector ){
var done = false;
@@ -634,9 +676,9 @@ SpaceGhost.prototype.tryClick = function tryClick( selector ){
return done;
};
// =================================================================== GALAXY CONVENIENCE
// =================================================================== MISCELAIN
/** Override capture to save to environ: GALAXY_TEST_SAVE (or passed in from CLI)
* @param {String} filename the image filename
@@ -651,6 +693,7 @@ SpaceGhost.prototype.capture = function capture( filename, clipRect_or_selector
/** 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
* @returns {Function[]} the array of functions no longer bound to the event
*/
SpaceGhost.prototype.popAllListeners = function popAllListeners( eventName ){
var returnedListeners = this.listeners( eventName );
@@ -660,7 +703,7 @@ SpaceGhost.prototype.popAllListeners = function popAllListeners( eventName ){
/** Add the given list of handler functions to the listener for eventName in order.
* @param {String} eventName the name of the event to which to add handlers
* @param {Array} handlerArray an array of event handler functions to add
* @param {Function[]} handlerArray an array of event handler functions to add
*/
SpaceGhost.prototype.addListeners = function addListeners( eventName, handlerArray ){
for( var i=0; i<handlerArray.length; i++ ){
@@ -668,14 +711,15 @@ SpaceGhost.prototype.addListeners = function addListeners( eventName, handlerArr
}
};
/** Send message to stderr
/** Send message to stderr using the phantom fs module.
* @param {String} the msg to output
*/
SpaceGhost.prototype.stderr = function( msg ){
var fs = require( 'fs' );
fs.write( '/dev/stderr', msg + '\n', 'w' );
};
// convenience logging funcs
// ------------------------------------------------------------------- convenience logging funcs
/** log using level = 'debug' and default namespace = 'spaceghost'
*/
SpaceGhost.prototype.debug = function( msg, namespace ){
@@ -690,14 +734,14 @@ SpaceGhost.prototype.info = function( msg, namespace ){
this.log( msg, 'info', namespace );
};
/** log using level = 'info' and default namespace = 'spaceghost'
/** log using level = 'warning' and default namespace = 'spaceghost'
*/
SpaceGhost.prototype.warning = function( msg, namespace ){
namespace = namespace || 'spaceghost';
this.log( msg, 'warning', namespace );
};
/** log using level = 'info' and default namespace = 'spaceghost'
/** log using level = 'error' and default namespace = 'spaceghost'
*/
SpaceGhost.prototype.error = function( msg, namespace ){
namespace = namespace || 'spaceghost';
@@ -707,6 +751,7 @@ SpaceGhost.prototype.error = function( msg, namespace ){
/** log despite logLevel settings, unless returnJsonOnly is set
*/
SpaceGhost.prototype.out = function( msg, namespace ){
namespace = namespace || 'spaceghost';
if( !this.options.returnJsonOnly ){
console.debug( msg );
}
@@ -718,10 +763,10 @@ SpaceGhost.prototype.jsonStr = function( obj ){
return JSON.stringify( obj, null, 2 );
};
/** output to debug the JSON of the selector (or null if not found)
/** output the JSON of the selector (or null if not found) to debug level
*/
SpaceGhost.prototype.debugElement = function debugElement( selector ){
this.debug( this.jsonStr( this.elementInfoOrNull( selector ) ) );
this.debug( selector + ':\n' + this.jsonStr( this.elementInfoOrNull( selector ) ) );
};
/** Debug SG itself
@@ -737,21 +782,6 @@ SpaceGhost.prototype.lastError = function(){
return this.errors[( this.errors.length - 1 )];
};
/** Get the last error from an assertRaises test (gen. for the message)
*/
SpaceGhost.prototype.getLastAssertRaisesError = function(){
// assuming the test passed here...
var testsThatPassed = this.test.testResults.passes;
var test = null;
for( var i=( testsThatPassed.length - 1 ); i>=0; i-- ){
currTest = testsThatPassed[i];
if( currTest.type === 'assertRaises' ){
test = currTest; break;
}
}
return ( ( test && test.values )?( test.values.error ):( undefined ) );
};
/** String representation
*/
SpaceGhost.prototype.toString = function(){
@@ -762,12 +792,105 @@ SpaceGhost.prototype.toString = function(){
return 'SpaceGhost(' + currentUrl + ')';
};
/** Load and parse a JSON file into an object.
* @param filepath filepath relative to the current scriptDir
* @returns the object parsed
*/
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 ) );
};
/** Load and parse a JSON file into an object.
* @param filepath filepath relative to the current scriptDir
* @param object the object to write
* @param mode 'w' for a new file, 'a' for append
*/
SpaceGhost.prototype.writeJSONFile = function writeJSONFile( filepath, object, mode ){
mode = mode || 'w';
//precondition: filepath is relative to script dir
filepath = this.options.scriptDir + filepath;
return require( 'fs' ).write( filepath, this.jsonStr( object ), mode );
};
// =================================================================== TEST DATA
// maintain selectors, labels, text here in one central location
/** General use selectors, labels, and text. Kept here to allow a centralized location.
*/
SpaceGhost.prototype.data = {
selectors : {
tooltipBalloon : '.bs-tooltip',
editableText : '.editable-text',
editableTextInput : 'input#renaming-active',
masthead : {
userMenu : {
userEmail : 'a #user-email',
userEmail_xpath : '//a[contains(text(),"Logged in as")]/span["id=#user-email"]'
}
},
frames : {
main : 'galaxy_main',
tools : 'galaxy_tools',
history : 'galaxy_history'
},
messages : {
all : '[class*="message"]',
error : '.errormessage',
done : '.donemessage',
donelarge : '.donemessagelarge'
},
loginPage : {
form : 'form#login',
submit_xpath : "//input[@value='Login']",
url_regex : /\/user\/login/
},
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?
}
}
},
labels : {
masthead : {
menus : {
user : 'User'
},
userMenu : {
register : 'Register',
login : 'Login',
logout : 'Logout'
}
},
tools : {
upload : {
panelLabel : 'Upload File'
}
}
},
text : {
registrationPage : {
badEmailError : 'Enter a real email address'
},
upload : {
success : 'The following job has been successfully added to the queue'
}
}
};
//TODO: to data
/*
SpaceGhost.prototype.selectors = {
tooltipBalloon : '.bs-tooltip',
editableText : '.editable-text',
editableTextInput : 'input#renaming-active',
masthead : {
userMenu : {
userEmail : 'a #user-email',
@@ -832,28 +955,14 @@ SpaceGhost.prototype.text = {
success : 'The following job has been successfully added to the queue'
}
};
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 ) );
};
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
/**
*/
exports.SpaceGhost = SpaceGhost;
exports.PageError = PageError;
exports.GalaxyError = GalaxyError;
exports.AlertError = AlertError;
/**
/** creation function
*/
exports.create = function create(options) {
"use strict";