1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/3bbbcdb044af/ Changeset: 3bbbcdb044af Branch: next-stable User: inithello Date: 2013-03-21 19:54:42 Summary: Merge in bugfix from next-stable. Affected #: 27 files diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/model/__init__.py --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1516,6 +1516,11 @@ # other model classes. hda = self rval = dict( id = hda.id, + uuid = ( lambda uuid: str( uuid ) if uuid else None )( hda.dataset.uuid ), + history_id = hda.history.id, + hid = hda.hid, + file_ext = hda.ext, + peek = ( lambda hda: hda.display_peek() if hda.peek and hda.peek != 'no peek' else None )( hda ), model_class = self.__class__.__name__, name = hda.name, deleted = hda.deleted, diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py +++ b/lib/galaxy/web/base/controller.py @@ -6,12 +6,16 @@ import os import re import pkg_resources +import urllib pkg_resources.require("SQLAlchemy >= 0.4") +pkg_resources.require( "Routes" ) +import routes from sqlalchemy import func, and_, select from paste.httpexceptions import HTTPBadRequest, HTTPInternalServerError, HTTPNotImplemented, HTTPRequestRangeNotSatisfiable from galaxy import util, web +from gettext import gettext from galaxy.datatypes.interval import ChromatinInteractions from galaxy.exceptions import ItemAccessibilityException, ItemDeletionException, ItemOwnershipException, MessageException from galaxy.security.validate_user_input import validate_publicname @@ -25,6 +29,11 @@ from galaxy.datatypes.data import Text +from galaxy.datatypes.display_applications import util as da_util +from galaxy.datatypes.metadata import FileParameter + +from galaxy.datatypes.display_applications.link_generator import get_display_app_link_generator + log = logging.getLogger( __name__ ) # States for passing messages @@ -322,6 +331,85 @@ return dataset.conversion_messages.PENDING return None + def get_hda_dict( self, trans, hda ): + hda_dict = hda.get_api_value( view='element' ) + history = hda.history + + # Add additional attributes that depend on trans can hence must be added here rather than at the model level. + if trans.user_is_admin() or trans.app.config.expose_dataset_path: + hda_dict[ 'file_name' ] = hda.file_name + + if not hda_dict[ 'deleted' ]: + hda_dict[ 'download_url' ] = url_for( 'history_contents_display', history_id = trans.security.encode_id( history.id ), + history_content_id = trans.security.encode_id( hda.id ) ) + + can_access_hda = trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset ) + hda_dict[ 'accessible' ] = ( trans.user_is_admin() or can_access_hda ) + hda_dict[ 'api_type' ] = "file" + + if not( hda.purged or hda.deleted or hda.dataset.purged ): + meta_files = [] + for meta_type in hda.metadata.spec.keys(): + if isinstance( hda.metadata.spec[ meta_type ].param, FileParameter ): + meta_files.append( dict( file_type=meta_type ) ) + if meta_files: + hda_dict[ 'meta_files' ] = meta_files + + hda_dict[ 'display_apps' ] = self.get_display_apps( trans, hda ) + hda_dict[ 'display_types' ] = self.get_old_display_applications( trans, hda ) + hda_dict[ 'visualizations' ] = hda.get_visualizations() + + if hda.creating_job and hda.creating_job.tool_id: + tool_used = trans.app.toolbox.get_tool( hda.creating_job.tool_id ) + if tool_used and tool_used.force_history_refresh: + hda_dict[ 'force_history_refresh' ] = True + + return trans.security.encode_dict_ids( hda_dict ) + + def get_display_apps( self, trans, hda ): + #TODO: make more straightforward (somehow) + display_apps = [] + + def get_display_app_url( display_app_link, hda, trans ): + web_url_for = routes.URLGenerator( trans.webapp.mapper, trans.environ ) + dataset_hash, user_hash = da_util.encode_dataset_user( trans, hda, None ) + return web_url_for( controller='dataset', + action="display_application", + dataset_id=dataset_hash, + user_id=user_hash, + app_name=urllib.quote_plus( display_app_link.display_application.id ), + link_name=urllib.quote_plus( display_app_link.id ) ) + + for display_app in hda.get_display_applications( trans ).itervalues(): + app_links = [] + for display_app_link in display_app.links.itervalues(): + app_links.append({ + 'target' : display_app_link.url.get( 'target_frame', '_blank' ), + 'href' : get_display_app_url( display_app_link, hda, trans ), + 'text' : gettext( display_app_link.name ) + }) + display_apps.append( dict( label=display_app.name, links=app_links ) ) + + return display_apps + + def get_old_display_applications( self, trans, hda ): + display_apps = [] + for display_app_name in hda.datatype.get_display_types(): + link_generator = get_display_app_link_generator( display_app_name ) + display_links = link_generator.generate_links( trans, hda ) + + app_links = [] + for display_name, display_link in display_links: + app_links.append({ + 'target' : '_blank', + 'href' : display_link, + 'text' : display_name + }) + if app_links: + display_apps.append( dict( label=hda.datatype.get_display_label( display_app_name ), links=app_links ) ) + + return display_apps + class UsesLibraryMixin: diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/galaxy/api/datasets.py --- a/lib/galaxy/webapps/galaxy/api/datasets.py +++ b/lib/galaxy/webapps/galaxy/api/datasets.py @@ -50,8 +50,11 @@ elif data_type == 'genome_data': rval = self._get_genome_data( trans, dataset, kwd.get('dbkey', None) ) else: - # Default: return dataset as API value. - rval = dataset.get_api_value() + # Default: return dataset as dict. + if hda_ldda == 'hda': + rval = self.get_hda_dict( trans, dataset ) + else: + rval = dataset.get_api_value() except Exception, e: rval = "Error in dataset API at listing contents: " + str( e ) diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/galaxy/api/history_contents.py --- a/lib/galaxy/webapps/galaxy/api/history_contents.py +++ b/lib/galaxy/webapps/galaxy/api/history_contents.py @@ -2,24 +2,12 @@ API operations on the contents of a history. """ import logging -import urllib -from gettext import gettext from galaxy import web from galaxy.web.base.controller import BaseAPIController, url_for from galaxy.web.base.controller import UsesHistoryDatasetAssociationMixin, UsesHistoryMixin from galaxy.web.base.controller import UsesLibraryMixin, UsesLibraryMixinItems -from galaxy.web.framework.helpers import to_unicode -from galaxy.datatypes.display_applications import util -from galaxy.datatypes.metadata import FileParameter - -from galaxy.datatypes.display_applications.link_generator import get_display_app_link_generator - -import pkg_resources -pkg_resources.require( "Routes" ) -import routes - log = logging.getLogger( __name__ ) class HistoryContentsController( BaseAPIController, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, @@ -67,7 +55,7 @@ if encoded_hda_id in ids: #TODO: share code with show try: - rval.append( get_hda_dict( trans, history, hda, for_editing=True ) ) + rval.append( self.get_hda_dict( trans, hda ) ) except Exception, exc: # don't fail entire list if hda err's, record and move on @@ -149,7 +137,7 @@ hda = self.get_history_dataset_association( trans, history, id, check_ownership=True, check_accessible=True ) - hda_dict = get_hda_dict( trans, history, hda, for_editing=True ) + hda_dict = self.get_hda_dict( trans, hda ) except Exception, e: msg = "Error in history API at listing dataset: %s" % ( str(e) ) @@ -175,107 +163,22 @@ if from_ld_id: try: ld = self.get_library_dataset( trans, from_ld_id, check_ownership=False, check_accessible=False ) - assert type( ld ) is trans.app.model.LibraryDataset, "Library content id ( %s ) is not a dataset" % from_ld_id + assert type( ld ) is trans.app.model.LibraryDataset, ( + "Library content id ( %s ) is not a dataset" % from_ld_id ) + except AssertionError, e: trans.response.status = 400 return str( e ) + except Exception, e: return str( e ) + hda = ld.library_dataset_dataset_association.to_history_dataset_association( history, add_to_history=True ) trans.sa_session.flush() return hda.get_api_value() + else: # TODO: implement other "upload" methods here. trans.response.status = 403 return "Not implemented." - -#TODO: move these into model -def get_hda_dict( trans, history, hda, for_editing ): - hda_dict = hda.get_api_value( view='element' ) - - hda_dict[ 'id' ] = trans.security.encode_id( hda.id ) - hda_dict[ 'history_id' ] = trans.security.encode_id( history.id ) - hda_dict[ 'hid' ] = hda.hid - - hda_dict[ 'file_ext' ] = hda.ext - if trans.user_is_admin() or trans.app.config.expose_dataset_path: - hda_dict[ 'file_name' ] = hda.file_name - - if not hda_dict[ 'deleted' ]: - hda_dict[ 'download_url' ] = url_for( 'history_contents_display', history_id = trans.security.encode_id( history.id ), history_content_id = trans.security.encode_id( hda.id ) ) - - can_access_hda = trans.app.security_agent.can_access_dataset( trans.get_current_user_roles(), hda.dataset ) - hda_dict[ 'accessible' ] = ( trans.user_is_admin() or can_access_hda ) - hda_dict[ 'api_type' ] = "file" - - if not( hda.purged or hda.deleted or hda.dataset.purged ): - meta_files = [] - for meta_type in hda.metadata.spec.keys(): - if isinstance( hda.metadata.spec[ meta_type ].param, FileParameter ): - meta_files.append( dict( file_type=meta_type ) ) - if meta_files: - hda_dict[ 'meta_files' ] = meta_files - - hda_dict[ 'display_apps' ] = get_display_apps( trans, hda ) - hda_dict[ 'display_types' ] = get_old_display_applications( trans, hda ) - - if hda.dataset.uuid is None: - hda_dict['uuid'] = None - else: - hda_dict['uuid'] = str(hda.dataset.uuid) - - hda_dict[ 'visualizations' ] = hda.get_visualizations() - if hda.peek and hda.peek != 'no peek': - hda_dict[ 'peek' ] = to_unicode( hda.display_peek() ) - - if hda.creating_job and hda.creating_job.tool_id: - tool_used = trans.app.toolbox.get_tool( hda.creating_job.tool_id ) - if tool_used and tool_used.force_history_refresh: - hda_dict[ 'force_history_refresh' ] = True - - return hda_dict - -def get_display_apps( trans, hda ): - #TODO: make more straightforward (somehow) - display_apps = [] - - def get_display_app_url( display_app_link, hda, trans ): - web_url_for = routes.URLGenerator( trans.webapp.mapper, trans.environ ) - dataset_hash, user_hash = util.encode_dataset_user( trans, hda, None ) - return web_url_for( controller='dataset', - action="display_application", - dataset_id=dataset_hash, - user_id=user_hash, - app_name=urllib.quote_plus( display_app_link.display_application.id ), - link_name=urllib.quote_plus( display_app_link.id ) ) - - for display_app in hda.get_display_applications( trans ).itervalues(): - app_links = [] - for display_app_link in display_app.links.itervalues(): - app_links.append({ - 'target' : display_app_link.url.get( 'target_frame', '_blank' ), - 'href' : get_display_app_url( display_app_link, hda, trans ), - 'text' : gettext( display_app_link.name ) - }) - display_apps.append( dict( label=display_app.name, links=app_links ) ) - - return display_apps - -def get_old_display_applications( trans, hda ): - display_apps = [] - for display_app_name in hda.datatype.get_display_types(): - link_generator = get_display_app_link_generator( display_app_name ) - display_links = link_generator.generate_links( trans, hda ) - - app_links = [] - for display_name, display_link in display_links: - app_links.append({ - 'target' : '_blank', - 'href' : display_link, - 'text' : display_name - }) - if app_links: - display_apps.append( dict( label=hda.datatype.get_display_label( display_app_name ), links=app_links ) ) - - return display_apps diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/galaxy/controllers/tool_runner.py --- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py +++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py @@ -312,4 +312,4 @@ <p><b>Please do not use your browser\'s "stop" or "reload" buttons until the upload is complete, or it may be interrupted.</b></p><p>You may safely continue to use Galaxy while the upload is in progress. Using "stop" and "reload" on pages other than Galaxy is also safe.</p> """ - return trans.show_message( msg, refresh_frames='history' ) + return trans.show_message( msg ) diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/tool_shed/api/repositories.py --- a/lib/galaxy/webapps/tool_shed/api/repositories.py +++ b/lib/galaxy/webapps/tool_shed/api/repositories.py @@ -1,24 +1,21 @@ +import logging import tool_shed.util.shed_util_common as suc from galaxy import web, util from galaxy.web.base.controller import BaseAPIController -from galaxy.web.framework.helpers import is_true - -import pkg_resources -pkg_resources.require( "Routes" ) -import routes -import logging log = logging.getLogger( __name__ ) + class RepositoriesController( BaseAPIController ): """RESTful controller for interactions with repositories in the Tool Shed.""" + @web.expose_api def index( self, trans, deleted=False, **kwd ): """ - GET /api/repository_revisions + GET /api/repositories Displays a collection (list) of repositories. """ - rval = [] + repository_dicts = [] deleted = util.string_as_bool( deleted ) try: query = trans.sa_session.query( trans.app.model.Repository ) \ @@ -28,30 +25,37 @@ for repository in query: value_mapper={ 'id' : trans.security.encode_id( repository.id ), 'user_id' : trans.security.encode_id( repository.user_id ) } - item = repository.get_api_value( view='collection', value_mapper=value_mapper ) - item[ 'url' ] = web.url_for( 'repository_contents', repository_id=trans.security.encode_id( repository.id ) ) - rval.append( item ) + repository_dict = repository.get_api_value( view='collection', value_mapper=value_mapper ) + repository_dict[ 'url' ] = web.url_for( controller='repository_contents', + action='index', + repository_id=trans.security.encode_id( repository.id ) ) + repository_dicts.append( repository_dict ) + return repository_dicts except Exception, e: message = "Error in the Tool Shed repositories API in index: %s" % str( e ) log.error( message, exc_info=True ) trans.response.status = 500 return message - return rval + @web.expose_api def show( self, trans, id, **kwd ): """ GET /api/repositories/{encoded_repository_id} Displays information about a repository in the Tool Shed. + + :param id: the encoded id of the `Repository` object """ try: repository = suc.get_repository_in_tool_shed( trans, id ) value_mapper={ 'id' : trans.security.encode_id( repository.id ), 'user_id' : trans.security.encode_id( repository.user_id ) } - repository_data = repository.get_api_value( view='element', value_mapper=value_mapper ) - repository_data[ 'contents_url' ] = web.url_for( 'repository_contents', repository_id=id ) + repository_dict = repository.get_api_value( view='element', value_mapper=value_mapper ) + repository_dict[ 'url' ] = web.url_for( controller='repository_contents', + action='index', + repository_id=trans.security.encode_id( repository.id ) ) + return repository_dict except Exception, e: message = "Error in the Tool Shed repositories API in show: %s" % str( e ) log.error( message, exc_info=True ) trans.response.status = 500 return message - return repository_data diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/tool_shed/api/repository_contents.py --- a/lib/galaxy/webapps/tool_shed/api/repository_contents.py +++ b/lib/galaxy/webapps/tool_shed/api/repository_contents.py @@ -3,33 +3,31 @@ from galaxy import web from galaxy.web.base.controller import BaseAPIController -import pkg_resources -pkg_resources.require( "Routes" ) -import routes - log = logging.getLogger( __name__ ) + class RepositoryContentsController( BaseAPIController ): + @web.expose_api def index( self, trans, **kwd ): """ - GET /api/repositories/{encoded_repository_id}/contents + GET /api/repositories/{encoded_repository_id} Displays a collection (dictionary) of repository contents. - :param repository_id: an encoded id string of the `Repository` to inspect + :param repository_id: the encoded id of the `Repository` object """ - rval = [] - repository_id = kwd.get( 'repository_id', None ) try: + repository_id = kwd[ 'repository_id' ] repository = suc.get_repository_in_tool_shed( trans, repository_id ) - value_mapper={ 'id' : repository_id, + value_mapper={ 'id' : trans.security.encode_id( repository.id ), 'user_id' : trans.security.encode_id( repository.user_id ) } repository_dict = repository.as_dict( value_mapper ) - repository_dict[ 'url' ] = web.url_for( 'repository_contents', repository_id=repository_id ) - rval.append( repository_dict ) + repository_dict[ 'url' ] = web.url_for( controller='repository_contents', + action='index', + repository_id=repository_id ) + return repository_dict except Exception, e: message = "Error in the Tool Shed repository_contents API in index: %s" % str( e ) log.error( message, exc_info=True ) trans.response.status = 500 return message - return rval diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/tool_shed/api/repository_revision_contents.py --- a/lib/galaxy/webapps/tool_shed/api/repository_revision_contents.py +++ b/lib/galaxy/webapps/tool_shed/api/repository_revision_contents.py @@ -1,14 +1,9 @@ import logging +from galaxy import web from galaxy.web.framework.helpers import time_ago -import tool_shed.util.shed_util_common as suc from tool_shed.util import metadata_util -from galaxy import web from galaxy.web.base.controller import BaseAPIController -import pkg_resources -pkg_resources.require( "Routes" ) -import routes - log = logging.getLogger( __name__ ) def default_value_mapper( trans, repository_metadata ): @@ -18,25 +13,27 @@ value_mapper[ 'time_last_tested' ] = time_ago( repository_metadata.time_last_tested ) return value_mapper + class RepositoryRevisionContentsController( BaseAPIController ): + @web.expose_api def index( self, trans, **kwd ): """ - GET /api/repository_revisions/{encoded_repository_metadata_id}/contents + GET /api/repository_revisions/{encoded_repository_metadata_id} Displays a collection (dictionary) of repository_metadata contents. - :param repository_metadata_id: an encoded id string of the `RepositoryMetadata` to inspect + :param repository_metadata_id: the encoded id of the `RepositoryMetadata` object """ - rval = [] - repository_metadata_id = kwd.get( 'repository_metadata_id', None ) try: + repository_metadata_id = kwd.get( 'repository_metadata_id', None ) repository_metadata = metadata_util.get_repository_metadata_by_id( trans, repository_metadata_id ) repository_dict = repository_metadata.as_dict( value_mapper=default_value_mapper( trans, repository_metadata ) ) - repository_dict[ 'url' ] = web.url_for( 'repository_revision_contents', repository_metadata_id=repository_metadata_id ) - rval.append( repository_dict ) + repository_dict[ 'url' ] = web.url_for( controller='repository_revision_contents', + action='index', + repository_metadata_id=repository_metadata_id ) + return repository_dict except Exception, e: - message = "Error in the Tool Shed repository_revision_contents API in index: %s" % str( e ) + message = "Error in the Tool Shed repository_revision_contents API in index: %s" % str( e ) log.error( message, exc_info=True ) trans.response.status = 500 return message - return rval diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/galaxy/webapps/tool_shed/api/repository_revisions.py --- a/lib/galaxy/webapps/tool_shed/api/repository_revisions.py +++ b/lib/galaxy/webapps/tool_shed/api/repository_revisions.py @@ -1,16 +1,9 @@ -import datetime +import datetime, logging from galaxy.web.framework.helpers import time_ago -import tool_shed.util.shed_util_common as suc from tool_shed.util import metadata_util from galaxy import web, util -from galaxy.model.orm import and_, or_ +from galaxy.model.orm import and_ from galaxy.web.base.controller import BaseAPIController -from galaxy.web.framework.helpers import is_true - -import pkg_resources -pkg_resources.require( "Routes" ) -import routes -import logging log = logging.getLogger( __name__ ) @@ -21,15 +14,17 @@ value_mapper[ 'time_last_tested' ] = time_ago( repository_metadata.time_last_tested ) return value_mapper + class RepositoryRevisionsController( BaseAPIController ): """RESTful controller for interactions with tool shed repository revisions.""" + @web.expose_api def index( self, trans, **kwd ): """ GET /api/repository_revisions Displays a collection (list) of repository revisions. """ - rval = [] + repository_metadata_dicts = [] # Build up an anded clause list of filters. clause_list = [] # Filter by downloadable if received. @@ -58,32 +53,40 @@ .order_by( trans.app.model.RepositoryMetadata.table.c.repository_id ) \ .all() for repository_metadata in query: - item = repository_metadata.get_api_value( view='collection', - value_mapper=default_value_mapper( trans, repository_metadata ) ) - item[ 'url' ] = web.url_for( 'repository_revision', id=trans.security.encode_id( repository_metadata.id ) ) - rval.append( item ) + repository_metadata_dict = repository_metadata.get_api_value( view='collection', + value_mapper=default_value_mapper( trans, repository_metadata ) ) + repository_metadata_dict[ 'url' ] = web.url_for( controller='repository_revision_contents', + action='index', + repository_metadata_id=trans.security.encode_id( repository_metadata.id ) ) + repository_metadata_dicts.append( repository_metadata_dict ) + return repository_metadata_dicts except Exception, e: - rval = "Error in the Tool Shed repository_revisions API in index: " + str( e ) - log.error( rval + ": %s" % str( e ) ) + message = "Error in the Tool Shed repository_revisions API in index: " + str( e ) + log.error( message, exc_info=True ) trans.response.status = 500 - return rval + return message + @web.expose_api def show( self, trans, id, **kwd ): """ GET /api/repository_revisions/{encoded_repository_metadata_id} Displays information about a repository_metadata record in the Tool Shed. + + :param id: the encoded id of the `RepositoryMetadata` object """ try: repository_metadata = metadata_util.get_repository_metadata_by_id( trans, id ) - repository_data = repository_metadata.get_api_value( view='element', - value_mapper=default_value_mapper( trans, repository_metadata ) ) - repository_data[ 'contents_url' ] = web.url_for( 'repository_revision_contents', repository_metadata_id=id ) + repository_metadata_dict = repository_metadata.as_dict( value_mapper=default_value_mapper( trans, repository_metadata ) ) + repository_metadata_dict[ 'url' ] = web.url_for( controller='repository_revision_contents', + action='index', + repository_metadata_id=trans.security.encode_id( repository_metadata.id ) ) + return repository_metadata_dict except Exception, e: message = "Error in the Tool Shed repository_revisions API in show: %s" % str( e ) log.error( message, exc_info=True ) trans.response.status = 500 return message - return repository_data + @web.expose_api def update( self, trans, payload, **kwd ): """ @@ -110,6 +113,8 @@ log.error( message, exc_info=True ) trans.response.status = 500 return message - item = repository_metadata.as_dict( value_mapper=default_value_mapper( trans, repository_metadata ) ) - item[ 'url' ] = web.url_for( 'repository_revision', id=repository_metadata_id ) - return [ item ] + repository_metadata_dict = repository_metadata.as_dict( value_mapper=default_value_mapper( trans, repository_metadata ) ) + repository_metadata_dict[ 'url' ] = web.url_for( controller='repository_revision_contents', + action='index', + repository_metadata_id=trans.security.encode_id( repository_metadata.id ) ) + return repository_metadata_dict diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 lib/tool_shed/scripts/api/tool_shed_repository_revision_update.py --- /dev/null +++ b/lib/tool_shed/scripts/api/tool_shed_repository_revision_update.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +""" +PUT/update script to update appropriate values in a repository_metadata table record in the Tool Shed. + +usage: tool_shed_repository_revision_update.py key url key1=value1 key2=value2 ... +""" + +import os, sys +sys.path.insert( 0, os.path.dirname( __file__ ) ) +from common import update + +import pkg_resources +pkg_resources.require( "simplejson" ) + +import simplejson + +to_json_string = simplejson.dumps +from_json_string = simplejson.loads + +data = {} +for key, value in [ kwarg.split( '=', 1 ) for kwarg in sys.argv[ 3: ] ]: + """ + This example script will properly handle updating the value of one or more of the following RepositoryMetadata attributes: + tools_functionally_correct, do_not_test, tool_test_errors + """ + if key in [ 'tools_functionally_correct', 'do_not_test' ]: + if str( value ).lower() in [ 'true', 'yes', 'on' ]: + new_value = True + else: + new_value = False + elif key in [ 'tool_test_errors' ]: + new_value = from_json_string( value ) + else: + new_value = str( value ) + data[ key ] = new_value + +update( sys.argv[ 1 ], sys.argv[ 2 ], data ) diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/anon-history-tests.js --- a/test/casperjs/anon-history-tests.js +++ b/test/casperjs/anon-history-tests.js @@ -68,15 +68,10 @@ }); // ------------------------------------------------------------------- check the empty history for well formedness -// grab the history frame bounds for mouse later tests -spaceghost.then( function(){ - historyFrameInfo = this.getElementInfo( 'iframe[name="galaxy_history"]' ); - //this.debug( 'historyFrameInfo:' + this.jsonStr( historyFrameInfo ) ); -}); - spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){ this.test.comment( 'history panel for anonymous user, new history' ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ + + this.withHistoryPanel( 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' ); @@ -103,15 +98,12 @@ 'Message contains "' + emptyMsgStr + '"' ); this.test.comment( 'name should have a tooltip with info on anon-user name editing' ); - // mouse over to find tooltip - this.historypanel.hoverOver( nameSelector, function testingHover(){ - this.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); - this.test.assertSelectorHasText( tooltipSelector, anonNameTooltip ); - }, historyFrameInfo ); + this.historypanel.hoverOver( nameSelector ); + this.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); + this.test.assertSelectorHasText( tooltipSelector, anonNameTooltip ); this.test.comment( 'name should NOT be editable when clicked by anon-user' ); - this.assertDoesntHaveClass( nameSelector, editableTextClass, - "Name field is not classed as editable text" ); + this.assertDoesntHaveClass( nameSelector, editableTextClass, "Name field is not classed as editable text" ); this.click( nameSelector ); this.test.assertDoesntExist( editableTextInput, "Clicking on name does not create an input" ); }); @@ -120,12 +112,13 @@ // ------------------------------------------------------------------- anon user can upload file spaceghost.then( function testAnonUpload(){ this.test.comment( 'anon-user should be able to upload files' ); + spaceghost.tools.uploadFile( filepathToUpload, function uploadCallback( _uploadInfo ){ - this.debug( 'uploaded HDA info: ' + this.jsonStr( _uploadInfo ) ); + this.debug( 'uploaded HDA info: ' + this.jsonStr( this.quickInfo( _uploadInfo.hdaElement ) ) ); 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 ); + this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.hdaElement.text ); testUploadInfo = _uploadInfo; }); }); @@ -134,19 +127,18 @@ this.test.assertNotVisible( emptyMsgSelector, 'Empty history message is not visible' ); }); - // ------------------------------------------------------------------- anon user can run tool on file // ------------------------------------------------------------------- anon user registers/logs in -> same history spaceghost.user.loginOrRegisterUser( email, password ); spaceghost.thenOpen( spaceghost.baseUrl, function(){ + this.test.comment( 'anon-user should login and be associated with previous history' ); - 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( spaceghost.data.selectors.frames.history, function(){ - var hdaInfo = this.historypanel.hdaElementInfoByTitle( testUploadInfo.name, testUploadInfo.hid ); + this.historypanel.waitForHdas( function(){ + var hdaInfo = this.historypanel.hdaElementInfoByTitle( testUploadInfo.hdaElement.text ); this.test.assert( hdaInfo !== null, "After logging in - found a matching hda by name and hid" ); if( hdaInfo ){ this.test.assert( testUploadInfo.hdaElement.attributes.id === hdaInfo.attributes.id, @@ -155,17 +147,18 @@ }); }); +// ------------------------------------------------------------------- logs out -> new history spaceghost.user.logout(); spaceghost.thenOpen( spaceghost.baseUrl, function(){ this.test.comment( 'logging out should create a new, anonymous history' ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ + + this.historypanel.waitForHdas( function(){ this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, 'Message contains "' + emptyMsgStr + '"' ); }); }); - // =================================================================== spaceghost.run( function(){ this.test.done(); diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/casperjs_runner.py --- a/test/casperjs/casperjs_runner.py +++ b/test/casperjs/casperjs_runner.py @@ -73,7 +73,7 @@ debug = False # bit of a hack - this is the beginning of the last string when capserjs --verbose=true --logLevel=debug # use this to get subprocess to stop waiting for output - casper_done_str = '# Tests complete' + casper_done_str = '# Stopping' # convert js test results to unittest.TestResults results_adapter = None #CasperJsonToUnittestResultsConverter() @@ -209,7 +209,8 @@ js_test_results = json.loads( results ) failures = js_test_results[ 'testResults' ][ 'failures' ] assert len( failures ) == 0, ( - "Some assertions failed in the headless browser tests (see the log for details)" ) + "%d assertions failed in the headless browser tests" %( len( failures ) ) + + " (see the log for details)" ) # ---------------------------------------------------------------- TestCase overrides def setUp( self ): @@ -299,10 +300,12 @@ # ==================================================================== TESTCASE EXAMPLE # these could be broken out into other py files - shouldn't be necc. ATM class Test_01_User( CasperJSTestCase ): - """TestCase that uses javascript and a headless browser to test dynamic pages. + """Tests for the Galaxy user centered functionality: + registration, login, etc. """ def test_10_registration( self ): - """User registration tests: register new user, logout, attempt bad registrations. + """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', @@ -331,7 +334,7 @@ class Test_03_HistoryPanel( CasperJSTestCase ): - """(Minimal) casperjs tests for tools. + """Tests for History fetching, rendering, and modeling. """ def test_00_history_panel( self ): """Test history panel basics (controls, structure, refresh, history options menu, etc.). @@ -339,13 +342,18 @@ self.run_js_script( 'history-panel-tests.js' ) def test_10_anonymous_histories( self ): + """Test history options button. + """ + self.run_js_script( 'history-options-tests.js' ) + + def test_20_anonymous_histories( self ): """Test history panel basics with an anonymous user. """ self.run_js_script( 'anon-history-tests.js' ) class Test_04_HDAs( CasperJSTestCase ): - """(Minimal) casperjs tests for tools. + """Tests for HistoryDatasetAssociation fetching, rendering, and modeling. """ def test_00_HDA_states( self ): """Test structure rendering of HDAs in all the possible HDA states diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/hda-state-tests.js --- a/test/casperjs/hda-state-tests.js +++ b/test/casperjs/hda-state-tests.js @@ -55,17 +55,10 @@ // start a new user spaceghost.user.loginOrRegisterUser( email, password ); -// 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 ) ); -}); - // upload a file spaceghost.then( function upload(){ spaceghost.tools.uploadFile( filepathToUpload, function uploadCallback( _uploadInfo ){ testUploadInfo = _uploadInfo; - this.info( 'testUploadInfo:' + this.jsonStr( testUploadInfo ) ); }); }); @@ -73,13 +66,12 @@ // =================================================================== TEST 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; +function testTitle( hdaSelector, name ){ + var titleSelector = hdaSelector + ' ' + this.historypanel.data.selectors.hda.title; this.test.assertVisible( titleSelector, 'HDA title is visible' ); - this.test.assertSelectorHasText( titleSelector, titleShouldBe, - 'HDA has proper hid and title' ); + this.test.assertSelectorHasText( titleSelector, name, + 'HDA contains name (' + name + '): ' + this.fetchText( titleSelector ) ); } function testTitleButtonStructure( hdaSelector, shouldHaveTheseButtons ){ @@ -87,28 +79,9 @@ 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 - } - }; + buttonsArea = hdaSelector + ' ' + this.historypanel.data.selectors.hda.titleButtonArea, + buttons = this.historypanel.data.hdaTitleButtons; + this.test.assertVisible( buttonsArea, 'Button area is visible' ); for( var i=0; i<shouldHaveTheseButtons.length; i++ ){ @@ -123,7 +96,7 @@ this.test.assertVisible( button.selector, buttonName + ' button is visible' ); var buttonElement = this.getElementInfo( button.selector ); - this.debug( 'buttonElement:' + this.jsonStr( buttonElement ) ); + this.debug( 'buttonElement:' + this.jsonStr( this.quickInfo( buttonElement ) ) ); // should be an anchor this.test.assert( buttonElement.nodeName === button.nodeName, @@ -135,12 +108,13 @@ 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 ); + this.historypanel.hoverOver( button.selector ); + 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 + '"' ); + // clear the tooltip + this.page.sendEvent( 'mouseover', 0, 0 ); } } @@ -185,6 +159,7 @@ 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' ); + //TODO: ... } function testSecondaryActionButtons( hdaSelector ){ @@ -193,6 +168,7 @@ 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' ); + //TODO: ... } function testPeek( hdaSelector, expectedPeekArray ){ @@ -239,112 +215,97 @@ // =================================================================== TESTS // ------------------------------------------------------------------- ok state -spaceghost.then( function checkOkState(){ +spaceghost.withHistoryPanel( function(){ this.test.comment( 'HDAs in the "ok" state should be well formed' ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ - var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; - this.test.assertVisible( uploadSelector, 'HDA is visible' ); + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + this.test.assertVisible( uploadSelector, 'HDA is visible' ); - this.test.comment( 'should have the proper state class' ); - this.assertHasClass( uploadSelector, this.historypanel.data.selectors.hda.wrapper.stateClasses.ok, - 'HDA has ok state class' ); + this.test.comment( 'should have the proper 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) + // 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 proper title and hid' ); + testTitle.call( spaceghost, uploadSelector, testUploadInfo.filename ); - this.test.comment( 'should have all of the three, main buttons' ); - testTitleButtonStructure.call( spaceghost, uploadSelector ); + 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( '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(){ + this.test.comment( 'clicking the hda title should expand its body' ); + this.historypanel.thenExpandHda( uploadSelector, function(){ + // ugh. + this.jumpToHistory( function(){ testExpandedBody.call( spaceghost, uploadSelector, summaryShouldBeArray, infoShouldBe, false ); }); }); }); +// restore to collapsed +spaceghost.then( function(){ + this.test.comment( "Collapsing hda in 'ok' state should hide body again" ); + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; -// 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' ); - }); + spaceghost.historypanel.thenCollapseHda( uploadSelector, function collapseOkState(){ + this.test.assertNotVisible( uploadSelector + ' ' + this.historypanel.data.selectors.hda.body, + 'body is not visible' ); }); }); +// ------------------------------------------------------------------- new state +spaceghost.withHistoryPanel( function(){ + // set state directly through model, wait for re-render + //TODO: not ideal to test this + this.evaluate( function(){ + return Galaxy.currHistoryPanel.model.hdas.at( 0 ).set( 'state', 'new' ); + }); + this.wait( 1000, function(){ + this.test.comment( 'HDAs in the "new" state should be well formed' ); -// ------------------------------------------------------------------- new state -spaceghost.then( function checkNewState(){ - this.test.comment( 'HDAs in the "new" state should be well formed' ); + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + this.test.assertVisible( uploadSelector, 'HDA is visible' ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ - // set state directly through model - //TODO: not ideal - this.evaluate( function(){ - return Galaxy.currHistoryPanel.model.hdas.at( 0 ).set( 'state', 'new' ); - }); - // wait for re-render - this.wait( 500, function(){ - var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; - this.test.assertVisible( uploadSelector, 'HDA is visible' ); + // should have proper title and hid + testTitle.call( spaceghost, uploadSelector, testUploadInfo.filename ); - // should have proper title and hid - testTitle.call( spaceghost, uploadSelector, testUploadInfo.hid, testUploadInfo.name ); + 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' ); - 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' ); + 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' ); - 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' ); + this.test.comment( 'clicking the title of the new HDA will expand the body' ); - 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)' ); + this.historypanel.thenExpandHda( uploadSelector, 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( hdaTitle ); - }); + 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 state, collapse -spaceghost.then( function revertStateAndCollapse(){ - this.withFrame( spaceghost.data.selectors.frames.history, function(){ +spaceghost.withHistoryPanel( function revertStateAndCollapse(){ + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + + this.historypanel.thenCollapseHda( uploadSelector, 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 ); - }); }); + this.wait( 1000 ); }); - +/* +*/ // =================================================================== spaceghost.run( function(){ diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/history-options-tests.js --- /dev/null +++ b/test/casperjs/history-options-tests.js @@ -0,0 +1,145 @@ +// have to handle errors here - or phantom/casper won't bail but _HANG_ +try { + var utils = require( 'utils' ), + xpath = require( 'casper' ).selectXPath, + format = utils.format, + + //...if there's a better way - please let me know, universe + scriptDir = require( 'system' ).args[3] + // remove the script filename + .replace( /[\w|\.|\-|_]*$/, '' ) + // if given rel. path, prepend the curr dir + .replace( /^(?!\/)/, './' ), + spaceghost = require( scriptDir + 'spaceghost' ).create({ + // script options here (can be overridden by CLI) + //verbose: true, + //logLevel: debug, + scriptDir: scriptDir + }); + + spaceghost.start(); + +} catch( error ){ + console.debug( error ); + phantom.exit( 1 ); +} + +// =================================================================== +/* TODO: + possibly break this file up +*/ +// =================================================================== globals and helpers +var email = spaceghost.user.getRandomEmail(), + password = '123456'; +if( spaceghost.fixtureData.testUser ){ + email = spaceghost.fixtureData.testUser.email; + password = spaceghost.fixtureData.testUser.password; + spaceghost.info( 'Will use fixtureData.testUser: ' + email ); +} + +// selectors and labels +var includeDeletedOptionsLabel = spaceghost.historyoptions.data.labels.options.includeDeleted; + +// local +var filepathToUpload = '../../test-data/1.txt', + testUploadInfo = {}; + + +// =================================================================== TESTS +// ------------------------------------------------------------------- set up +// start a new user +spaceghost.user.loginOrRegisterUser( email, password ); + +spaceghost.tools.uploadFile( filepathToUpload, function uploadCallback( _uploadInfo ){ + testUploadInfo = _uploadInfo; +}); + +// ------------------------------------------------------------------- history options menu structure +//NOTE: options menu should be functionally tested elsewhere +spaceghost.historypanel.waitForHdas().then( function checkHistoryOptions(){ + this.test.comment( 'History options icon should be in place and menu should have the proper structure' ); + + // 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 + var historyOptions = this.historyoptions.data.labels.options; + for( var optionKey in historyOptions ){ + if( historyOptions.hasOwnProperty( optionKey ) ){ + var optionLabel = historyOptions[ optionKey ]; + this.test.assertVisible( this.historyoptions.data.selectors.optionXpathByLabelFn( optionLabel ), + 'Option label is visible: ' + optionLabel ); + } + } + + // clear the menu + this.click( 'body' ); + this.test.assertNotVisible( this.historyoptions.data.selectors.menu, + "Clicking away from the menu closes it" ); +}); + +// ------------------------------------------------------------------- options allow showing/hiding deleted hdas +spaceghost.then( function(){ + this.test.comment( 'Deleting HDA' ); + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + + this.historypanel.deleteHda( uploadSelector, function(){ + this.test.assertNotExists( uploadSelector, "Deleted HDA is NOT in the DOM" ); + }); +}); + +spaceghost.then( function(){ + this.test.comment( 'History options->' + includeDeletedOptionsLabel + ' shows deleted datasets' ); + var uploadSelector = '#' + testUploadInfo.hdaElement.attributes.id; + + this.historyoptions.includeDeleted( function(){ + this.test.assertExists( uploadSelector, + "Deleted HDA is in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" ); + this.test.assertVisible( uploadSelector, + "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.excludeDeleted( function(){ + this.test.assertDoesntExist( '#' + testUploadInfo.hdaElement.attributes.id, + "Deleted HDA is not in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" ); + }); + // undelete the uploaded file + this.historypanel.undeleteHda( '#' + testUploadInfo.hdaElement.attributes.id ); +}); + +// ------------------------------------------------------------------- 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 + + +// ------------------------------------------------------------------- history options collapses all expanded hdas +spaceghost.then( function(){ + this.historypanel.thenExpandHda( '#' + testUploadInfo.hdaElement.attributes.id ); +}); +spaceghost.then( function(){ + this.test.comment( 'History option collapses all expanded hdas' ); + + this.historyoptions.collapseExpanded( function(){ + var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id; + this.withHistoryPanel( function(){ + this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, + "Body for uploaded file is not visible" ); + }); + }); +}); + +// =================================================================== +spaceghost.run( function(){ + this.test.done(); +}); diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/history-panel-tests.js --- a/test/casperjs/history-panel-tests.js +++ b/test/casperjs/history-panel-tests.js @@ -72,177 +72,164 @@ // ------------------------------------------------------------------- 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 + '"' ); + +// ------------------------------------------------------------------- check structure of empty history +spaceghost.thenOpen( spaceghost.baseUrl ).historypanel.waitForHdas( function(){ + this.test.comment( 'history panel with a new, empty history should be well formed' ); + this.test.comment( "frame should have proper url and title: 'History'" ); + this.test.assertMatch( this.getCurrentUrl(), /\/history/, 'Found history frame url' ); + this.test.assertTitle( this.getTitle(), 'History', 'Found history frame title' ); + + this.test.comment( "history name should exist, be visible, and have text " + unnamedName ); + this.test.assertExists( nameSelector, nameSelector + ' exists' ); + this.test.assertVisible( nameSelector, 'History name is visible' ); + this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); + + this.test.comment( "history subtitle should display size and size should be: " + initialSizeStr ); + this.test.assertExists( subtitleSelector, 'Found ' + subtitleSelector ); + this.test.assertVisible( subtitleSelector, 'History subtitle is visible' ); + this.test.assertSelectorHasText( subtitleSelector, initialSizeStr, + 'History subtitle has "' + initialSizeStr + '"' ); + + this.test.comment( "tags and annotation icons should be available" ); + this.test.assertExists( tagIconSelector, 'Tag icon button found' ); + this.test.assertExists( annoIconSelector, 'Annotation icon button found' ); + + this.test.comment( "A message about the current history being empty should be displayed" ); + this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); + this.test.assertVisible( emptyMsgSelector, 'Empty history message is visible' ); + this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, + 'Message contains "' + emptyMsgStr + '"' ); }); -// 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 ) ); -}); +// ------------------------------------------------------------------- name editing +spaceghost.withHistoryPanel( function(){ + this.test.comment( 'history panel, editing the history name' ); -// ------------------------------------------------------------------- check structure of empty history -spaceghost.thenOpen( spaceghost.baseUrl, function testPanelStructure(){ - this.test.comment( 'history panel, new history' ); - 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' ); + this.test.comment( 'name should have a tooltip with proper info on name editing' ); + this.historypanel.hoverOver( nameSelector ); + this.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); + this.test.assertSelectorHasText( tooltipSelector, nameTooltip ); + // clear the tooltip + this.page.sendEvent( 'mousemove', -1, -1 ); - this.test.comment( "history name should exist, be visible, and have text " + unnamedName ); - this.test.assertExists( nameSelector, nameSelector + ' exists' ); - this.test.assertVisible( nameSelector, 'History name is visible' ); - this.test.assertSelectorHasText( nameSelector, unnamedName, 'History name is ' + unnamedName ); + this.test.comment( 'name should be create an input when clicked' ); + this.assertHasClass( nameSelector, editableTextClass, "Name field classed for editable text" ); + this.click( nameSelector ); + this.test.assertExists( editableTextInput, "Clicking on name creates an input" ); - 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, - 'History subtitle has "' + initialSizeStr + '"' ); - - this.test.comment( "tags and annotation icons should be available" ); - this.test.assertExists( tagIconSelector, 'Tag icon button found' ); - this.test.assertExists( annoIconSelector, 'Annotation icon button found' ); - - this.test.comment( "A message about the current history being empty should be displayed" ); - this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); - this.test.assertVisible( emptyMsgSelector, 'Empty history message is visible' ); - this.test.assertSelectorHasText( emptyMsgSelector, emptyMsgStr, - 'Message contains "' + emptyMsgStr + '"' ); + 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 + this.page.sendEvent( 'keypress', newHistoryName ); + this.page.sendEvent( 'keypress', this.page.event.key.Enter ); + // wait for send and re-render name + this.wait( 1000, function(){ + this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is ' + newHistoryName ); + this.test.assertDoesntExist( editableTextInput, "Input disappears after pressing enter" ); }); }); -// ------------------------------------------------------------------- name editing -spaceghost.then( function(){ - this.test.comment( 'history panel, editing the history name' ); - 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.test.assertExists( tooltipSelector, "Found tooltip after name hover" ); - this.test.assertSelectorHasText( tooltipSelector, nameTooltip ); +spaceghost.withHistoryPanel( function(){ + this.test.comment( 'name should revert if user clicks away while editing' ); - this.test.comment( 'name should be create an input when clicked' ); - this.assertHasClass( nameSelector, editableTextClass, "Name field classed for editable text" ); - this.click( nameSelector ); - this.test.assertExists( editableTextInput, "Clicking on name creates an input" ); + this.click( nameSelector ); + this.page.sendEvent( 'keypress', "Woodchipper metagenomics, Fargo, ND" ); - 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 - 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( editableTextInput, "Input disappears after pressing enter" ); - }); + this.page.sendEvent( 'mousedown', -1, -1 ); + this.wait( 1000, function(){ + this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is STILL ' + newHistoryName ); + this.test.assertDoesntExist( editableTextInput, "Input disappears after clicking away" ); }); }); -spaceghost.then( 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( editableTextInput ); - this.page.sendEvent( 'mousedown', historyFrameInfo.x + inputInfo.x + 1, historyFrameInfo.y + inputInfo.y - 5 ); +spaceghost.withHistoryPanel( function(){ + this.test.comment( 'name should revert if user hits ESC while editing' ); - this.wait( 1000, function(){ - this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is STILL ' + newHistoryName ); - this.test.assertDoesntExist( editableTextInput, "Input disappears after clicking away" ); - }); - }); -}); -spaceghost.then( 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" ); + this.click( nameSelector ); + this.page.sendEvent( 'keypress', "Arsenic Bacteria" ); - this.page.sendEvent( 'keypress', this.page.event.key.Escape ); - this.wait( 1000, function(){ - this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is STILL ' + newHistoryName ); - this.test.assertDoesntExist( editableTextInput, "Input disappears after hitting ESC" ); - }); + 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( editableTextInput, "Input disappears after hitting ESC" ); }); }); // ------------------------------------------------------------------- check structure of NON empty history // upload file: 1.txt -spaceghost.then( function upload(){ +spaceghost.tools.uploadFile( filepathToUpload, function uploadCallback( _uploadInfo ){ this.test.comment( 'uploaded file should appear in history' ); - 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( wrapperOkClassName ) !== -1; - this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name ); - testUploadInfo = _uploadInfo; - }); + + this.debug( 'uploaded HDA info: ' + this.jsonStr( _uploadInfo ) ); + var hasHda = _uploadInfo.hdaElement, + hasClass = _uploadInfo.hdaElement.attributes[ 'class' ], + hasOkClass = _uploadInfo.hdaElement.attributes[ 'class' ].indexOf( wrapperOkClassName ) !== -1; + this.test.assert( ( hasHda && hasClass && hasOkClass ), "Uploaded file: " + _uploadInfo.name ); + testUploadInfo = _uploadInfo; }); -spaceghost.then( function checkPanelStructure(){ +spaceghost.withHistoryPanel( function checkPanelStructure(){ this.test.comment( 'checking structure of non-empty panel' ); - 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' ); - this.test.assertSelectorHasText( nameSelector, newHistoryName, 'History name is ' + newHistoryName ); + 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 ); - 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 + '"' ); + 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 + '": ' + this.fetchText( subtitleSelector ).trim() ); - this.test.comment( "tags and annotation icons should be available" ); - this.test.assertExists( tagIconSelector, 'Tag icon button found' ); - this.test.assertExists( annoIconSelector, 'Annotation icon button found' ); + this.test.comment( "tags and annotation icons should be available" ); + this.test.assertExists( tagIconSelector, 'Tag icon button found' ); + this.test.assertExists( annoIconSelector, 'Annotation icon button found' ); - this.test.comment( "A message about the current history being empty should NOT be displayed" ); - this.test.assertExists( emptyMsgSelector, emptyMsgSelector + ' exists' ); - this.test.assertNotVisible( emptyMsgSelector, 'Empty history message is NOT visible' ); - }); + 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' ); }); // ------------------------------------------------------------------- tags // keeping this light here - better for it's own test file //TODO: check tooltips -spaceghost.then( function openTags(){ +spaceghost.withHistoryPanel( function openTags(){ this.test.comment( 'tag area should open when the history panel tag icon is clicked' ); - 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' ); - }); + + this.click( tagIconSelector ); + this.wait( 1000, function(){ + this.test.assertVisible( tagAreaSelector, 'Tag area is now displayed' ); + }); +}); +spaceghost.withHistoryPanel( function closeAnnotation(){ + this.test.comment( 'annotation area should close when the history panel tag icon is clicked again' ); + + this.click( tagIconSelector ); + this.wait( 1000, function(){ + this.test.assertNotVisible( tagAreaSelector, 'Tag area is now hidden' ); }); }); // ------------------------------------------------------------------- annotation // keeping this light here - better for it's own test file //TODO: check tooltips -spaceghost.then( function openAnnotation(){ +spaceghost.withHistoryPanel( function openAnnotation(){ this.test.comment( 'annotation area should open when the history panel annotation icon is clicked' ); - 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' ); - }); + + this.click( annoIconSelector ); + this.wait( 1000, function(){ + this.test.assertVisible( annoAreaSelector, 'Annotation area is now displayed' ); }); }); -spaceghost.then( function closeAnnotation(){ +spaceghost.withHistoryPanel( function closeAnnotation(){ this.test.comment( 'annotation area should close when the history panel tag icon is clicked again' ); - 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' ); - }); + + this.click( annoIconSelector ); + this.wait( 1000, function(){ + this.test.assertNotVisible( annoAreaSelector, 'Annotation area is now hidden' ); }); }); @@ -261,96 +248,16 @@ }); }); -// ------------------------------------------------------------------- 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' ); - - // 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( '#' + testUploadInfo.hdaElement.attributes.id, function(){ - this.test.assertDoesntExist( '#' + testUploadInfo.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( spaceghost.data.selectors.frames.history, function(){ - this.waitForSelector( nameSelector, function(){ - this.test.assertExists( '#' + testUploadInfo.hdaElement.attributes.id, - "Deleted HDA is in the DOM (using history options -> " + includeDeletedOptionsLabel + ")" ); - this.test.assertVisible( '#' + testUploadInfo.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( spaceghost.data.selectors.frames.history, function(){ - this.waitForSelector( nameSelector, function(){ - this.test.assertDoesntExist( '#' + testUploadInfo.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( spaceghost.data.selectors.frames.history, function(){ - this.waitForSelector( nameSelector, function(){ - //TODO: to conv. fn - this.click( '#' + testUploadInfo.hdaElement.attributes.id - + ' ' + this.historypanel.data.selectors.history.undeleteLink ); - }); - }); -}); - -// ------------------------------------------------------------------- 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(){ +spaceghost.historypanel.waitForHdas( function(){ this.test.comment( 'HDAs can be expanded by clicking on the name' ); var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id; - this.withFrame( spaceghost.data.selectors.frames.history, function(){ - this.click( uploadedSelector + ' .historyItemTitle' ); - this.wait( 1000, function(){ - this.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, - "Body for uploaded file is visible" ); - }); + this.click( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.title ); + this.wait( 1000, function(){ + this.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, + "Body for uploaded file is visible" ); }); }); @@ -360,26 +267,22 @@ var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id; this.click( refreshButtonSelector ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ - this.waitForSelector( nameSelector, function(){ - this.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, - "Body for uploaded file is visible" ); - }); + this.historypanel.waitForHdas( function(){ + this.test.assertVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, + "Body for uploaded file is visible" ); }); // this will break: webkit + jq 1.7 }); // ------------------------------------------------------------------- expanded hdas collapse by clicking name again -spaceghost.then( function(){ +spaceghost.withHistoryPanel( function(){ this.test.comment( 'Expanded hdas collapse by clicking name again' ); 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 + ' ' + this.historypanel.data.selectors.hda.body, - "Body for uploaded file is not visible" ); - }); + this.click( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.title ); + this.wait( 500, function(){ + this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, + "Body for uploaded file is not visible" ); }); }); @@ -389,32 +292,9 @@ var uploadedSelector = '#' + testUploadInfo.hdaElement.attributes.id; this.click( refreshButtonSelector ); - this.withFrame( spaceghost.data.selectors.frames.history, function(){ - this.waitForSelector( nameSelector, function(){ - this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, - "Body for uploaded file is not visible" ); - }); - }); -}); - -// ------------------------------------------------------------------- history options collapses all expanded hdas -spaceghost.then( function(){ - // expand again - 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 = '#' + testUploadInfo.hdaElement.attributes.id; - - this.historyoptions.collapseExpanded(); - this.wait( 500, function(){ - 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" ); - }); + this.historypanel.waitForHdas( function(){ + this.test.assertNotVisible( uploadedSelector + ' ' + this.historypanel.data.selectors.hda.body, + "Body for uploaded file is not visible" ); }); }); diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/login-tests.js --- a/test/casperjs/login-tests.js +++ b/test/casperjs/login-tests.js @@ -39,6 +39,8 @@ password = spaceghost.fixtureData.testUser.password; } +var userEmailSelector = '//a[contains(text(),"Logged in as")]/span["id=#user-email"]'; + // =================================================================== TESTS // register a user (again...) spaceghost.thenOpen( spaceghost.baseUrl, function(){ @@ -52,8 +54,7 @@ spaceghost.user.logout(); }); spaceghost.then( function(){ - this.test.assertSelectorDoesntHaveText( - xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), /\w/ ); + this.test.assertSelectorDoesntHaveText( xpath( userEmailSelector ), /\w/ ); this.test.assert( spaceghost.user.loggedInAs() === '', 'loggedInAs() is empty string' ); }); @@ -63,8 +64,7 @@ spaceghost.user._submitLogin( email, password ); //No such user }); spaceghost.thenOpen( spaceghost.baseUrl, function(){ - this.test.assertSelectorHasText( - xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), email ); + this.test.assertSelectorHasText( xpath( userEmailSelector ), email ); this.test.assert( spaceghost.user.loggedInAs() === email, 'loggedInAs() matches email' ); }); diff -r 24d068f7a44dab10f8574d15aa4063ccdb0edb22 -r 3bbbcdb044afdbd380d6cd4c0f721ab7262c5555 test/casperjs/modules/api.js --- /dev/null +++ b/test/casperjs/modules/api.js @@ -0,0 +1,231 @@ +// =================================================================== module object, exports +/** Creates a new api module object. + * @param {SpaceGhost} spaceghost a spaceghost instance + * @exported + */ +exports.create = function createAPI( spaceghost, apikey ){ + return new API( spaceghost ); +}; + +/** User object constructor. + * @param {SpaceGhost} spaceghost a spaceghost instance + * @param {String} apikey apikey for use when not using session authentication + */ +var API = function API( spaceghost, apikey ){ + this.spaceghost = spaceghost; + this.apikey = apikey; + + this.encodedIdExpectedLength = 16; + this.jQueryLocation = '../../static/scripts/libs/jquery/jquery.js'; + + this.histories = new HistoriesAPI( this ); + this.hdas = new HDAAPI( this ); +}; +exports.API = API; + +API.prototype.toString = function toString(){ + return ( this.spaceghost + '.API:' + + (( this.apikey )?( this.apikey ):( '(session)' )) ); +}; + +// ------------------------------------------------------------------- APIError +APIError.prototype = new Error(); +APIError.prototype.constructor = Error; +/** @class Thrown when Galaxy the API returns an error from a request */ +function APIError( msg ){ + Error.apply( this, arguments ); + this.name = "APIError"; + this.message = msg; +} +exports.APIError = APIError; + +/* ------------------------------------------------------------------- TODO: + can we component-ize this to become the basis for js-based api binding/resource + +*/ +// =================================================================== INTERNAL +var utils = require( 'utils' ); + +API.prototype._ajax = function _ajax( url, options ){ + options = options || {}; + options.async = false; + + this.ensureJQuery( '../../static/scripts/libs/jquery/jquery.js' ); + var resp = this.spaceghost.evaluate( function( url, options ){ + return jQuery.ajax( url, options ); + }, url, options ); + //this.spaceghost.debug( 'resp: ' + this.spaceghost.jsonStr( resp ) ); + + if( resp.status !== 200 ){ + // grrr... this doesn't lose the \n\r\t + throw new APIError( resp.responseText.replace( /[\s\n\r\t]+/gm, ' ' ).replace( /"/, '' ) ); + } + return JSON.parse( resp.responseText ); +}; + +// =================================================================== MISC +API.prototype.isEncodedId = function isEncodedId( id ){ + if( typeof id !== 'string' ){ return false; } + if( id.match( /[g-zG-Z]/ ) ){ return false; } + return ( id.length === this.encodedIdExpectedLength ); +}; + +// ------------------------------------------------------------------- is type or throw err +API.prototype.ensureId = function ensureId( id ){ + if( !this.isEncodedId( id ) ){ + throw new APIError( 'ID is not a valid encoded id: ' + id ); + } + return id; +}; + +API.prototype.ensureObject = function ensureObject( obj ){ + if( !utils.isObject( obj ) ){ + throw new APIError( 'Not a valid object: ' + obj ); + } + return obj; +}; + +// ------------------------------------------------------------------- jquery +// using jq for the ajax in this module - that's why these are here +//TODO:?? could go in spaceghost +API.prototype.hasJQuery = function hasJQuery(){ + return this.spaceghost.evaluate( function pageHasJQuery(){ + var has = false; + try { + has = typeof ( jQuery + '' ) === 'string'; + } catch( err ){} + return has; + }); +}; + +API.prototype.ensureJQuery = function ensureJQuery(){ + if( !this.hasJQuery() ){ + var absLoc = this.spaceghost.options.scriptDir + this.jQueryLocation, + injected = this.spaceghost.page.injectJs( absLoc ); + if( !injected ){ + throw new APIError( 'Could not inject jQuery' ); + } + } +}; + + +// =================================================================== HISTORIES +var HistoriesAPI = function HistoriesAPI( api ){ + this.api = api; +}; +HistoriesAPI.prototype.toString = function toString(){ + return this.api + '.HistoriesAPI'; +}; + +// ------------------------------------------------------------------- +HistoriesAPI.prototype.urlTpls = { + index : 'api/histories', + show : 'api/histories/%s', + create : 'api/histories', + delete_ : 'api/histories/%s', + undelete: 'api/histories/deleted/%s/undelete' +}; + +HistoriesAPI.prototype.index = function index( deleted ){ + this.api.spaceghost.info( 'history.index: ' + (( deleted )?( 'w deleted' ):( '(wo deleted)' )) ); + + deleted = deleted || false; + return this.api._ajax( this.urlTpls.index, { + data : { deleted: deleted } + }); +}; + +HistoriesAPI.prototype.show = function show( id, deleted ){ + this.api.spaceghost.info( 'history.show: ' + [ id, (( deleted )?( 'w deleted' ):( '' )) ] ); + + id = ( id === 'most_recently_used' )?( id ):( this.api.ensureId( id ) ); + deleted = deleted || false; + return this.api._ajax( utils.format( this.urlTpls.show, id ), { + data : { deleted: deleted } + }); +}; + +HistoriesAPI.prototype.create = function create( payload ){ + this.api.spaceghost.info( 'history.create: ' + this.api.spaceghost.jsonStr( payload ) ); + + // py.payload <-> ajax.data + payload = this.api.ensureObject( payload ); + return this.api._ajax( utils.format( this.urlTpls.create ), { + type : 'POST', + data : payload + }); +}; + +HistoriesAPI.prototype.delete_ = function delete_( id, purge ){ + this.api.spaceghost.info( 'history.delete: ' + [ id, (( purge )?( '(purge!)' ):( '' )) ] ); + + // py.payload <-> ajax.data + var payload = ( purge )?({ purge: true }):({}); + return this.api._ajax( utils.format( this.urlTpls.delete_, this.api.ensureId( id ) ), { + type : 'DELETE', + data : payload + }); +}; + +HistoriesAPI.prototype.undelete = function undelete( id ){ + //throw ( 'unimplemented' ); + this.api.spaceghost.info( 'history.undelete: ' + id ); + + return this.api._ajax( utils.format( this.urlTpls.undelete, this.api.ensureId( id ) ), { + type : 'POST' + }); +}; + + +// =================================================================== HDAS +var HDAAPI = function HDAAPI( api ){ + this.api = api; +}; +HDAAPI.prototype.toString = function toString(){ + return this.api + '.HDAAPI'; +}; + +// ------------------------------------------------------------------- +HDAAPI.prototype.urlTpls = { + index : 'api/histories/%s/contents', + show : 'api/histories/%s/contents/%s', + create : 'api/histories/%s/contents'//, + // not implemented + //delete_ : 'api/histories/%s', + //undelete: 'api/histories/deleted/%s/undelete' +}; + +HDAAPI.prototype.index = function index( historyId, ids ){ + this.api.spaceghost.info( 'history.index: ' + [ historyId, ids ] ); + var data = {}; + if( ids ){ + ids = ( utils.isArray( ids ) )?( ids.join( ',' ) ):( ids ); + data.ids = ids; + } + + return this.api._ajax( utils.format( this.urlTpls.index, this.api.ensureId( historyId ) ), { + data : data + }); +}; + +HDAAPI.prototype.show = function show( historyId, id, deleted ){ + this.api.spaceghost.info( 'history.show: ' + [ id, (( deleted )?( 'w deleted' ):( '' )) ] ); + + id = ( id === 'most_recently_used' )?( id ):( this.api.ensureId( id ) ); + deleted = deleted || false; + return this.api._ajax( utils.format( this.urlTpls.show, id ), { + data : { deleted: deleted } + }); +}; + +HDAAPI.prototype.create = function create( historyId, payload ){ + this.api.spaceghost.info( 'history.create: ' + this.api.spaceghost.jsonStr( payload ) ); + + // py.payload <-> ajax.data + payload = this.api.ensureObject( payload ); + return this.api._ajax( utils.format( this.urlTpls.create ), { + type : 'POST', + data : payload + }); +}; + This diff is so big that we needed to truncate the remainder. 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.