galaxy-commits
Threads by month
- ----- 2026 -----
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- 15302 discussions
commit/galaxy-central: inithello: Tool shed functional tests for malformed XML in tool_dependencies.xml.
by Bitbucket 12 Feb '13
by Bitbucket 12 Feb '13
12 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/04b8060562f8/
changeset: 04b8060562f8
user: inithello
date: 2013-02-12 21:00:28
summary: Tool shed functional tests for malformed XML in tool_dependencies.xml.
affected #: 3 files
diff -r 01e73b11a46f87b03af29581603378b06187051d -r 04b8060562f865fa5b3ec6dac4ecfe590bd3ec07 test/tool_shed/functional/test_0010_repository_with_tool_dependencies.py
--- a/test/tool_shed/functional/test_0010_repository_with_tool_dependencies.py
+++ b/test/tool_shed/functional/test_0010_repository_with_tool_dependencies.py
@@ -63,20 +63,28 @@
'freebayes/sam_fa_indices.loc.sample',
strings_displayed=[],
commit_message='Uploaded tool data table .loc file.' )
- def test_0025_upload_invalid_tool_dependency_xml( self ):
+ def test_0025_upload_malformed_tool_dependency_xml( self ):
+ '''Upload tool_dependencies.xml with bad characters in the readme tag.'''
+ repository = test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
+ self.upload_file( repository,
+ os.path.join( 'freebayes', 'malformed_tool_dependencies', 'tool_dependencies.xml' ),
+ valid_tools_only=False,
+ strings_displayed=[ 'Exception attempting to parse tool_dependencies.xml', 'not well-formed' ],
+ commit_message='Uploaded malformed tool dependency XML.' )
+ def test_0030_upload_invalid_tool_dependency_xml( self ):
'''Upload tool_dependencies.xml defining version 0.9.5 of the freebayes package.'''
repository = test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
os.path.join( 'freebayes', 'invalid_tool_dependencies', 'tool_dependencies.xml' ),
strings_displayed=[ 'Name, version and type from a tool requirement tag does not match' ],
commit_message='Uploaded invalid tool dependency XML.' )
- def test_0030_upload_valid_tool_dependency_xml( self ):
+ def test_0035_upload_valid_tool_dependency_xml( self ):
'''Upload tool_dependencies.xml defining version 0.9.4_9696d0ce8a962f7bb61c4791be5ce44312b81cf8 of the freebayes package.'''
repository = test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.upload_file( repository,
os.path.join( 'freebayes', 'tool_dependencies.xml' ),
commit_message='Uploaded valid tool dependency XML.' )
- def test_0035_verify_tool_dependencies( self ):
+ def test_0040_verify_tool_dependencies( self ):
'''Verify that the uploaded tool_dependencies.xml specifies the correct package versions.'''
repository = test_db_util.get_repository_by_name_and_owner( repository_name, common.test_user_1_name )
self.display_manage_repository_page( repository,
diff -r 01e73b11a46f87b03af29581603378b06187051d -r 04b8060562f865fa5b3ec6dac4ecfe590bd3ec07 test/tool_shed/functional/test_1010_install_repository_with_tool_dependencies.py
--- a/test/tool_shed/functional/test_1010_install_repository_with_tool_dependencies.py
+++ b/test/tool_shed/functional/test_1010_install_repository_with_tool_dependencies.py
@@ -57,6 +57,11 @@
commit_message="Uploaded invalid_tool_dependencies/tool_dependencies.xml.",
remove_repo_files_not_in_tar='No' )
self.upload_file( repository,
+ os.path.join( 'freebayes', 'malformed_tool_dependencies', 'tool_dependencies.xml' ),
+ valid_tools_only=False,
+ strings_displayed=[ 'Exception attempting to parse tool_dependencies.xml', 'not well-formed' ],
+ commit_message='Uploaded malformed tool dependency XML.' )
+ self.upload_file( repository,
'freebayes/tool_dependencies.xml',
valid_tools_only=False,
commit_message="Uploaded tool_dependencies.xml",
diff -r 01e73b11a46f87b03af29581603378b06187051d -r 04b8060562f865fa5b3ec6dac4ecfe590bd3ec07 test/tool_shed/test_data/freebayes/malformed_tool_dependencies/tool_dependencies.xml
--- /dev/null
+++ b/test/tool_shed/test_data/freebayes/malformed_tool_dependencies/tool_dependencies.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<tool_dependency>
+ <package name="freebayes" version="0.9.5">
+ <install version="1.0">
+ <actions>
+ <action type="shell_command">git clone --recursive git://github.com/ekg/freebayes.git</action>
+ <action type="shell_command">git reset --hard 9696d0ce8a962f7bb61c4791be5ce44312b81cf8</action>
+ <action type="shell_command">make</action>
+ <action type="move_directory_files">
+ <source_directory>bin</source_directory>
+ <destination_directory>$INSTALL_DIR/bin</destination_directory>
+ </action>
+ <action type="set_environment">
+ <environment_variable name="PATH" action="prepend_to">$INSTALL_DIR/bin</environment_variable>
+ </action>
+ </actions>
+ </install>
+ <readme>
+FreeBayes requires g++ and the standard C and C++ development libraries.
+Additionally, cmake is required for building the BamTools API.
+ </readme>
+ </package>
+ <package name="samtools" version="0.2.15">
+ <install version="1.0">
+ <actions>
+ <action type="download_by_url">http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1…</action>
+ <action type="shell_command">sed -i .bak -e 's/-lcurses/-lncurses/g' Makefile</action>
+ <action type="shell_command">make</action>
+ <action type="move_file">
+ <source>samtools</source>
+ <destination>$INSTALL_DIR/bin</destination>
+ </action>
+ <action type="move_file">
+ <source>misc/maq2sam-long</source>
+ <destination>$INSTALL_DIR/bin</destination>
+ </action>
+ <action type="set_environment">
+ <environment_variable name="PATH" action="prepend_to">$INSTALL_DIR/bin</environment_variable>
+ </action>
+ </actions>
+ </install>
+ <readme>
+This readme tag has invalid XML ><
+ </readme>
+ </package>
+</tool_dependency>
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.
1
0
commit/galaxy-central: carlfeberhard: Incorporate headless browser testing using CasperJS (casperjs.org) into galaxy functional testing
by Bitbucket 12 Feb '13
by Bitbucket 12 Feb '13
12 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/01e73b11a46f/
changeset: 01e73b11a46f
user: carlfeberhard
date: 2013-02-12 20:36:48
summary: Incorporate headless browser testing using CasperJS (casperjs.org) into galaxy functional testing
affected #: 7 files
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/casperjs_runner.py
--- /dev/null
+++ b/test/casperjs/casperjs_runner.py
@@ -0,0 +1,324 @@
+"""Test runner for casperjs headless browser tests with the Galaxy distribution.
+
+Allows integration of casperjs tests with buildbot, run_functional_tests.sh
+
+Tests can be run in any of the following ways:
+* casperjs mytests.js --url='http://localhost:8080'
+* python casperjs_runner.py
+* nosetests
+* sh run_functional_tests.sh test/casperjs/test_runner
+* sh run_functional_tests.sh
+
+Note: that you can enable (lots) of debugging info using cli options:
+* casperjs usertests.js --url='http://localhost:8080' --verbose=true --logLevel=debug
+
+(see casperjs.org for more information)
+"""
+# -------------------------------------------------------------------- can't do 2.5
+import sys
+( major, minor, micro, releaselevel, serial ) = sys.version_info
+if minor < 6:
+ msg = 'casperjs requires python 2.6 or newer. Using: %s' %( sys.version )
+ try:
+ # if nose is installed do a skip test
+ from nose.plugins.skip import SkipTest
+ raise SkipTest( msg )
+ except ImportError, i_err:
+ raise AssertionError( msg )
+
+# --------------------------------------------------------------------
+import os
+import subprocess
+import json
+import errno
+import re
+
+import unittest
+from server_env import TestEnvironment
+
+import pprint
+import logging
+logging.basicConfig( stream=sys.stderr, name=__name__ )
+log = logging.getLogger( __name__ )
+
+# ==================================================================== MODULE VARS
+_PATH_TO_HEADLESS = 'casperjs'
+
+_TODO = """
+ get data back from js scripts (uploaded files, etc.)
+ use returned json to output list of failed assertions if code == 2
+"""
+
+# ====================================================================
+class HeadlessJSJavascriptError( Exception ):
+ """An error that occurrs in the javascript test file.
+ """
+ pass
+
+class CasperJSTestCase( unittest.TestCase ):
+ """Casper tests running in a unittest framework.
+ """
+ # casper uses a lot of escape codes to colorize output - these capture those and allow removal
+ escape_code_compiled_pattern = None
+ escape_code_pattern = r'\x1b\[[\d|;]+m'
+
+ # info on where to get casper js - shown when the exec can't be found
+ casper_info = """
+ CasperJS is a navigation scripting & testing utility for PhantomJS, written in Javascript.
+ More information is available at: casperjs.org
+ """
+
+ # debugging flag - set to true to have casperjs tests output with --verbose=true and --logLevel=debug
+ 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 = '[info] [phantom] Done'
+
+ # convert js test results to unittest.TestResults
+ results_adapter = None #CasperJsonToUnittestResultsConverter()
+
+ # ---------------------------------------------------------------- run the js script
+ def run_js_script( self, rel_script_path, *args, **kwargs ):
+ """Start the headless browser tests in a separate process and use both
+ the subprocess return code and the stdout output (formatted as JSON)
+ to determine which tests failed and which passed.
+ """
+ log.debug( 'beginning headless browser tests: %s', rel_script_path )
+ process_command_list = self.build_command_line( rel_script_path, *args, **kwargs )
+ log.debug( 'process_command_list: %s', str( process_command_list ) )
+ try:
+ process = subprocess.Popen( process_command_list, shell=False,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE )
+
+ # output from the browser (stderr only) immediately
+ while process.poll() == None:
+ stderr_msg = process.stderr.readline()
+ stderr_msg = self.strip_escape_codes( stderr_msg.strip() )
+ log.debug( '(%s): %s', rel_script_path, stderr_msg )
+ if stderr_msg.startswith( self.casper_done_str ):
+ break
+
+ # stdout is assumed to have the json test data/results
+ ( stdout_output, stderr_output ) = process.communicate()
+ #log.debug( '%s stdout output:\n%s', rel_script_path, stdout_output )
+ #log.debug( '%s stderr output:\n%s', rel_script_path, stderr_output )
+
+ log.debug( 'process.returncode: %d', process.returncode )
+ if process.returncode == 1:
+ #TODO: this is a fail on first effect
+ raise self.browser_error_to_exception( rel_script_path, stdout_output )
+
+ # couldn't find the headless browser,
+ # provide information (as it won't be included by default with galaxy)
+ except OSError, os_err:
+ if os_err.errno == errno.ENOENT:
+ log.error( 'No path to headless browser executable: %s\n'
+ + 'These tests were designed to use the following headless browser:\n%s',
+ self.exec_path, self.casper_info )
+ raise
+
+ self.handle_js_results( stdout_output )
+
+ def build_command_line( self, rel_script_path, *args, **kwargs ):
+ """Build the headless browser command line list for subprocess.
+ """
+ command_line_list = [ self.exec_path ]
+
+ # make rel_script_path an absolute path (when this is not run from it's dir - i.e. run_functional_tests.sh)
+ curr_dir = os.path.dirname( __file__ )
+ script_path = os.path.join( curr_dir, rel_script_path )
+ command_line_list.append( script_path )
+
+ # let browser know where the server is (from the TestEnvironment created in setUp)
+ command_line_list.append( '--url=' + self.env.url )
+
+ # add the return json only option
+ # - has script send normal output to stderr and results, errors, logs to stdout as json
+ command_line_list.append( '--return-json' )
+
+ # check flag to output (very) verbose debugging messages from casperjs and tests
+ if self.debug:
+ command_line_list.extend([ '--verbose=true', '--logLevel=debug' ])
+
+ #TODO: allow casperjs cli options ('--includes='), ?in args, kwargs?
+ command_line_list.extend( args )
+
+ # send extra data - encode kwargs as json to pass to casper for decoding
+ command_line_list.append( json.dumps( kwargs ) )
+ return command_line_list
+
+ def strip_escape_codes( self, msg ):
+ """Removes colorizing escape codes from casper output strings.
+ """
+ if not self.escape_code_compiled_pattern:
+ self.escape_code_compiled_pattern = re.compile( self.escape_code_pattern )
+ return re.sub( self.escape_code_compiled_pattern, '', msg )
+
+ # ---------------------------------------------------------------- convert js error to python error
+ def browser_error_to_exception( self, script_path, stdout_output ):
+ """Converts the headless' error from JSON into a more informative
+ python HeadlessJSJavascriptError.
+ """
+ get_error = lambda d: d[ 'errors' ][0]
+ get_msg = lambda err: err[ 'msg' ]
+ get_trace = lambda err: err[ 'backtrace' ]
+ try:
+ # assume it's json and located in errors (and first)
+ js_test_results = json.loads( stdout_output )
+ last_error = get_error( js_test_results )
+ err_string = ( "%s\n%s" %( get_msg( last_error ),
+ self.browser_backtrace_to_string( get_trace( last_error ) ) ) )
+
+ # if we couldn't parse json from what's returned on the error, raise a vanilla exc
+ except Exception, exc:
+ log.debug( '(failed to parse error returned from %s: %s)', _PATH_TO_HEADLESS, str( exc ) )
+ return HeadlessJSJavascriptError(
+ "ERROR in headless browser script %s" %( script_path ) )
+
+ # otherwise, raise with msg and backtrace
+ return HeadlessJSJavascriptError( err_string )
+
+ def browser_backtrace_to_string( self, backtrace ):
+ """Converts list of trace dictionaries (as might be returned from
+ json results) to a string similar to a python backtrace.
+ """
+ template = ' File "%s", line %s, in %s'
+ traces = []
+ for trace in backtrace:
+ traces.append( template %( trace[ 'file' ], trace[ 'line' ], trace[ 'function' ] ) )
+ return '\n'.join( traces )
+
+ # ---------------------------------------------------------------- results
+ def handle_js_results( self, results ):
+ """Handle the results of the js tests by either converting them
+ with the results adapter or checking for a failure list.
+ """
+ # if given an adapter - use it
+ if self.results_adapter:
+ self.results_adapter.convert( results, self )
+
+ # - otherwise, assert no failures found
+ else:
+ 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)" )
+
+ # ---------------------------------------------------------------- TestCase overrides
+ def setUp( self ):
+ # set up the env for each test
+ self.env = TestEnvironment.instance()
+ self.exec_path = _PATH_TO_HEADLESS
+
+ def run( self, result=None ):
+ # wrap this in order to save ref to result
+ #TODO: gotta be a better way
+ self.result = result
+ unittest.TestCase.run( self, result=result )
+
+
+# ==================================================================== RESULTS CONVERSION
+class CasperJsonToUnittestResultsConverter( object ):
+ """Convert casper failures, success to individual unittest.TestResults
+ """
+ #TODO: So far I can add result instances - but each has the id, shortDescription
+ # of the TestCase.testMethod that called it. Can't find out how to change these.
+
+ def convert( self, json_results, test ):
+ """Converts JSON test results into unittest.TestResults.
+
+ precondition: test should have attribute 'result' which
+ is a unittest.TestResult (for that test).
+ """
+ results_dict = json.loads( json_results )
+ failures = results_dict[ 'testResults' ][ 'failures' ]
+ passes = results_dict[ 'testResults' ][ 'passes' ]
+ self.add_json_failures_to_results( failures, test )
+ self.add_json_successes_to_results( passes, test )
+
+ def add_json_failures_to_results( self, failures, test ):
+ """Converts JSON test failures.
+ """
+ #precondition: result should be an attr of test (a TestResult)
+ #TODO: no way to change test.desc, name in output?
+ for failure in failures:
+ #TODO: doesn't change shortDescription
+ #if 'standard' in failure:
+ # self.__doc__ = failure[ 'standard' ]
+ test.result.addFailure( test, self.casper_failure_to_unittest_failure( failure ) )
+ test.result.testsRun += 1
+
+ def casper_failure_to_unittest_failure( self, casper_failure, failure_class=AssertionError ):
+ """Returns a casper test failure (in dictionary form) as a 3-tuple of
+ the form used by unittest.TestResult.addFailure.
+
+ Used to add failures to a casperjs TestCase.
+ """
+ #TODO: this is all too elaborate
+ fail_type = casper_failure[ 'type' ]
+ values = json.dumps( casper_failure[ 'values' ] )
+ desc = casper_failure[ 'standard' ]
+ if 'messgae' in casper_failure:
+ desc = capser_failure[ 'message' ]
+ failure_msg = "(%s) %s: %s" %( fail_type, desc, values )
+ #TODO: tb is empty ([]) - can we get file info from casper, covert to py trace?
+ return ( failure_class, failure_msg, [] )
+
+ def add_json_successes_to_results( self, successes, test ):
+ """Converts JSON test successes.
+ """
+ for success in successes:
+ ## attempt to re-write test result description - doesn't work
+ #if 'standard' in success:
+ # self.__doc__ = success[ 'standard' ]
+ test.result.addSuccess( test )
+ test.result.testsRun += 1
+
+
+# ==================================================================== MODULE FIXTURE
+#NOTE: nose will run these automatically
+def setup_module():
+ log.debug( '\n--------------- setting up module' )
+
+def teardown_module():
+ log.debug( '\n--------------- tearing down module' )
+
+
+# ==================================================================== TESTCASE EXAMPLE
+# these could be broken out into other files - shouldn't be necc. ATM
+class UserTests( CasperJSTestCase ):
+ """TestCase that uses javascript and a headless browser to test dynamic pages.
+ """
+ def test_10_registration( self ):
+ """User registration tests: register new user, logout, attempt bad registrations.
+ """
+ # all keywords will be compiled into a single JSON obj and passed to the server
+ self.run_js_script( 'registration-tests.js', self.env.url,
+ testuser={ 'email': 'test1(a)test.test', 'password': '123456' })
+ #TODO:?? could theoretically do db cleanup, checks here with SQLALX
+ #TODO: have run_js_script return other persistant fixture data (uploaded files, etc.)
+
+ def test_20_login( self ):
+ """User log in tests.
+ """
+ self.run_js_script( 'login-tests.js', self.env.url,
+ testuser={ 'email': 'test1(a)test.test', 'password': '123456' })
+
+
+class ToolTests( CasperJSTestCase ):
+ """(Minimal) casperjs tests for tools.
+ """
+ #debug = True
+ def test_10_upload( self ):
+ """Tests uploading files
+ """
+ self.run_js_script( 'upload-tests.js' )
+
+
+# ==================================================================== MAIN
+if __name__ == '__main__':
+ log.setLevel( logging.DEBUG )
+ setup_module()
+ #TODO: server_env config doesn't work with unittest's lame main fn
+ unittest.main()
+ # teardown_module() isn't called when unittest.main is used
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/login-tests.js
--- /dev/null
+++ b/test/casperjs/login-tests.js
@@ -0,0 +1,142 @@
+// have to handle errors here - or phantom/casper won't bail but _HANG_
+//TODO: global error handler?
+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:
+ move selectors and assertText strings into global object for easier editing
+
+
+*/
+// =================================================================== globals and helpers
+var email = spaceghost.getRandomEmail(),
+ password = '123456';
+if( spaceghost.fixtureData.testUser ){
+ email = spaceghost.fixtureData.testUser.email;
+ password = spaceghost.fixtureData.testUser.password;
+}
+
+// =================================================================== TESTS
+spaceghost.thenOpen( spaceghost.baseUrl, function(){
+ this.test.comment( 'loading galaxy homepage' );
+ // can we load galaxy?
+ this.test.assertTitle( 'Galaxy' );
+});
+
+// ------------------------------------------------------------------- should work
+
+// register a user (again...)
+spaceghost.then( function(){
+ this.test.comment( 'registering: ' + email );
+ spaceghost.registerUser( email, password );
+});
+// capture a sshot
+//spaceghost.then( function(){
+// this.clickLabel( 'User' );
+// this.capture( 'register.png' );
+//});
+
+// log them out - check for empty logged in text
+spaceghost.then( function(){
+ this.test.comment( 'logging out: ' + email );
+ spaceghost.logout();
+});
+spaceghost.then( function(){
+ this.test.assertSelectorDoesntHaveText(
+ xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), /\w/ );
+ this.test.assert( spaceghost.loggedInAs() === '', 'loggedInAs() is empty string' );
+});
+
+// log them back in - check for email in logged in text
+spaceghost.then( function(){
+ this.test.comment( 'logging back in: ' + email );
+ spaceghost._submitLogin( email, password ); //No such user
+});
+spaceghost.then( function(){
+ this.test.assertSelectorHasText(
+ xpath( '//a[contains(text(),"Logged in as")]/span["id=#user-email"]' ), email );
+ this.test.assert( spaceghost.loggedInAs() === email, 'loggedInAs() matches email' );
+});
+
+// finally log back out for next tests
+spaceghost.then( function(){
+ this.test.comment( 'logging out: ' + email );
+ spaceghost.logout();
+});
+
+// ------------------------------------------------------------------- shouldn't work
+// can't log in: users that don't exist, bad emails, sql injection (hurhur)
+var badEmails = [ 'test2(a)test.org', 'test', '', "'; SELECT * FROM galaxy_user WHERE 'u' = 'u';" ];
+spaceghost.each( badEmails, function( self, badEmail ){
+ self.then( function(){
+ this.test.comment( 'attempting bad email: ' + badEmail );
+ this._submitLogin( badEmail, password );
+ });
+ self.then(function(){
+ this.assertErrorMessage( 'No such user' );
+ });
+});
+
+// can't use passwords that wouldn't be accepted in registration
+var badPasswords = [ '1234', '', '; SELECT * FROM galaxy_user' ];
+spaceghost.each( badPasswords, function( self, badPassword ){
+ self.then( function(){
+ this.test.comment( 'attempting bad password: ' + badPassword );
+ this._submitLogin( email, badPassword );
+ });
+ self.then(function(){
+ this.assertErrorMessage( 'Invalid password' );
+ });
+});
+
+// ------------------------------------------------------------------- test yoself
+// these versions are for conv. use in other tests, they should throw errors if used improperly
+spaceghost.then( function(){
+ this.assertStepsRaise( 'GalaxyError: LoginError', function(){
+ this.then( function(){
+ this.test.comment( 'testing (js) error thrown on bad email' );
+ this.login( 'nihilist', '1234' );
+ });
+ });
+});
+
+spaceghost.then( function(){
+ this.assertStepsRaise( 'GalaxyError: LoginError', function(){
+ this.then( function(){
+ this.test.comment( 'testing (js) error thrown on bad password' );
+ this.login( email, '1234' );
+ });
+ });
+});
+
+spaceghost.then( function(){
+ this.logout();
+});
+
+// ===================================================================
+spaceghost.run( function(){
+ this.test.done();
+});
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/registration-tests.js
--- /dev/null
+++ b/test/casperjs/registration-tests.js
@@ -0,0 +1,182 @@
+// 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:
+ move selectors and assertText strings into global object for easier editing
+ pass email, etc. for first (successful) registration (for use with other tests)
+
+
+*/
+// =================================================================== globals and helpers
+var email = spaceghost.getRandomEmail(),
+ password = '123456',
+ confirm = password,
+ username = 'test' + Date.now();
+
+// =================================================================== TESTS
+spaceghost.thenOpen( spaceghost.baseUrl, function(){
+ this.test.comment( 'loading galaxy homepage' );
+ // can we load galaxy?
+ this.test.assertTitle( 'Galaxy' );
+ // xpath selector use:
+ this.test.assertExists( xpath( "//div[@id='masthead']" ), 'found masthead' );
+});
+
+// failing tests for...testing...the tests
+//spaceghost.thenOpen( spaceghost.baseUrl, function(){
+// this.test.comment( 'loading galaxy homepage' );
+// // can we load galaxy?
+// this.test.assertTitle( 'Blorgo' );
+// // xpath selector use:
+// this.test.assertExists( xpath( "//div[@id='facebook']" ), 'found facebook' );
+//});
+
+
+// ------------------------------------------------------------------- register a new user
+spaceghost.then( function(){
+ this.test.comment( 'registering user: ' + email );
+ this._submitUserRegistration( email, password, username, confirm );
+});
+spaceghost.thenOpen( spaceghost.baseUrl, function(){
+ this.clickLabel( 'User' );
+ this.test.assertSelectorHasText( 'a #user-email', email, '#user-email === ' + email );
+});
+
+
+// ------------------------------------------------------------------- log out that user
+spaceghost.then( function(){
+ this.test.comment( 'logging out user: ' + email );
+ this.logout();
+});
+spaceghost.then( function(){
+ this.debug( 'email:' + this.getElementInfo( 'a #user-email' ).html );
+ this.test.assert( !this.getElementInfo( 'a #user-email' ).html, '#user-email is empty' );
+});
+
+
+// ------------------------------------------------------------------- bad user registrations
+spaceghost.then( function(){
+ this.test.comment( 'attempting to re-register user: ' + email );
+ this._submitUserRegistration( email, password, username, confirm );
+});
+spaceghost.then(function(){
+ this.assertErrorMessage( 'User with that email already exists' );
+});
+
+// emails must be in the form -(a)-.- (which is an email on main, btw)
+var badEmails = [ 'bob', 'bob@', 'bob@idontwanttocleanup', 'bob.cantmakeme' ];
+spaceghost.each( badEmails, function( self, badEmail ){
+ self.then( function(){
+ this.test.comment( 'attempting bad email: ' + badEmail );
+ this._submitUserRegistration( badEmail, password, username, confirm );
+ });
+ self.then(function(){
+ this.assertErrorMessage( 'Enter a real email address' );
+ });
+});
+
+// passwords must be at least 6 chars long
+var badPasswords = [ '1234' ];
+spaceghost.each( badPasswords, function( self, badPassword ){
+ self.then( function(){
+ this.test.comment( 'attempting bad password: ' + badPassword );
+ this._submitUserRegistration( spaceghost.getRandomEmail(), badPassword, username, confirm );
+ });
+ self.then(function(){
+ this.assertErrorMessage( 'Use a password of at least 6 characters' );
+ });
+});
+
+// and confirm must match
+var badConfirms = [ '1234', '12345678', '123456 7', '' ];
+spaceghost.each( badConfirms, function( self, badConfirm ){
+ self.then( function(){
+ this.test.comment( 'attempting bad password confirmation: ' + badConfirm );
+ this._submitUserRegistration( spaceghost.getRandomEmail(), password, username, badConfirm );
+ });
+ self.then(function(){
+ this.assertErrorMessage( 'Passwords do not match' );
+ });
+});
+
+// usernames must be >=4 chars...
+//NOTE: that short username errors only show AFTER checking for existing/valid emails
+// so: we need to generate new emails for each one
+spaceghost.then( function(){
+ var newEmail = spaceghost.getRandomEmail(),
+ badUsername = 'bob';
+ this.test.comment( 'attempting short username: ' + badUsername );
+ this._submitUserRegistration( newEmail, password, badUsername, confirm );
+});
+spaceghost.then(function(){
+ this.assertErrorMessage( 'Public name must be at least 4 characters in length' );
+});
+
+// ...and be lower-case letters, numbers and '-'...
+var badUsernames = [ 'BOBERT', 'Robert Paulson', 'bobert!', 'bob_dobbs' ];
+spaceghost.each( badUsernames, function( self, badUsername ){
+ self.then( function(){
+ var newEmail = spaceghost.getRandomEmail();
+ this.test.comment( 'attempting bad username: ' + badUsername );
+ this._submitUserRegistration( newEmail, password, badUsername, confirm );
+ });
+ self.then(function(){
+ this.assertErrorMessage( "Public name must contain only lower-case letters, numbers and '-'" );
+ });
+});
+
+// ...and the name can't be used already
+spaceghost.then( function(){
+ var newEmail = spaceghost.getRandomEmail();
+ this.test.comment( 'attempting previously used username with new user: ' + newEmail );
+ this._submitUserRegistration( newEmail, password, username, confirm );
+});
+spaceghost.then(function(){
+ this.assertErrorMessage( 'Public name is taken; please choose another' );
+});
+
+
+// ------------------------------------------------------------------- test the tests
+// these versions are for conv. use in other tests, they should throw errors if used improperly
+spaceghost.then( function(){
+ this.assertStepsRaise( 'GalaxyError: RegistrationError', function(){
+ this.then( function(){
+ this.test.comment( 'testing (js) error thrown on bad email' );
+ this.registerUser( '@internet', '123456', 'ignobel' );
+ });
+ });
+});
+
+spaceghost.then( function(){
+ this.logout();
+});
+
+// ===================================================================
+spaceghost.run( function(){
+ this.test.done();
+});
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/server_env.py
--- /dev/null
+++ b/test/casperjs/server_env.py
@@ -0,0 +1,97 @@
+"""
+Classes to handle fetching the proper environment and urls for the selenium
+tests to run against.
+"""
+
+import os
+import logging
+log = logging.getLogger( __name__ )
+
+class TestEnvironment( object ):
+ """Provides basic information on the server being tested.
+
+ Implemented as a singleton class so that it may persist between tests
+ without needing to be reset/re-created.
+ """
+ _instance = None
+
+ ENV_PROTOCOL = None
+ ENV_HOST = 'GALAXY_TEST_HOST'
+ ENV_PORT = 'GALAXY_TEST_PORT'
+ ENV_HISTORY_ID = 'GALAXY_TEST_HISTORY_ID'
+ ENV_FILE_DIR = 'GALAXY_TEST_FILE_DIR'
+ ENV_TOOL_SHED_TEST_FILE = 'GALAXY_TOOL_SHED_TEST_FILE'
+ ENV_SAVED_FILES_DIR = 'GALAXY_TEST_SAVE'
+
+ DEFAULT_PROTOCOL = 'http'
+ DEFAULT_HOST = 'localhost'
+ DEFAULT_PORT = '8080'
+
+ @classmethod
+ def instance( cls, config=None ):
+ # singleton pattern
+ if( ( not cls._instance )
+ or ( config ) ):
+ log.debug( 'creating singleton instance of "%s", config: %s', str( cls ), str( config ) )
+ cls._instance = cls( config )
+ return cls._instance
+
+ @classmethod
+ def get_server_url( cls ):
+ return cls.instance().url
+
+ def __init__( self, env_config_dict=None ):
+ self.config = env_config_dict or {}
+
+ self.protocol = self._get_setting_from_config_or_env( 'protocol', self.ENV_PROTOCOL, self.DEFAULT_PROTOCOL )
+ self.host = self._get_setting_from_config_or_env( 'host', self.ENV_HOST, self.DEFAULT_HOST )
+ self.port = self._get_setting_from_config_or_env( 'port', self.ENV_PORT, self.DEFAULT_PORT )
+
+ self.history_id = self._get_setting_from_config_or_env( 'history_id', self.ENV_HISTORY_ID, default=None )
+ self.file_dir = self._get_setting_from_config_or_env( 'file_dir', self.ENV_FILE_DIR, default=None )
+
+ self.tool_shed_test_file = self._get_setting_from_config_or_env(
+ 'tool_shed_test_file', self.ENV_TOOL_SHED_TEST_FILE, default=None )
+ self.shed_tools_dict = self._get_shed_tools_dict()
+
+ self.keepOutdir = self._get_setting_from_config_or_env( 'keepOutdir', self.ENV_SAVED_FILES_DIR, default=None )
+ self._init_saved_files_dir()
+
+ def _get_setting_from_config_or_env( self, config_name, env_name, default=False ):
+ """Try to get a setting from (in order):
+ TestEnvironment.config, the os env, or some default (if not False).
+ """
+ config = self.config.get( config_name, None )
+ env = os.environ.get( env_name, None )
+ if( ( not ( config or env ) )
+ and ( default == False ) ):
+ raise AttributeError( '"%s" was not set via config or %s or default' %( config_name, env_name ) )
+ return config or env or default
+
+ def _get_shed_tools_dict( self ):
+ """Read the shed tools from the tool shed test file if given,
+ otherwise an empty dict.
+ """
+ if self.tool_shed_test_file:
+ f = open( self.tool_shed_test_file, 'r' )
+ text = f.read()
+ f.close()
+ return from_json_string( text )
+ else:
+ return {}
+
+ def _init_saved_files_dir( self ):
+ """Set up the desired directory to save test output
+ """
+ if self.keepOutdir > '':
+ try:
+ os.makedirs( self.keepOutdir )
+ except:
+ log.debug( 'unable to create saved files directory: %s' %( self.keepOutDir ) )
+
+ @property
+ def url( self ):
+ url = '%s://%s' %( self.protocol, self.host )
+ if self.port and self.port != 80:
+ url += ':%s' %( str( self.port ) )
+ return url
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/spaceghost.js
--- /dev/null
+++ b/test/casperjs/spaceghost.js
@@ -0,0 +1,1030 @@
+/* TODO:
+ Use in test command
+
+ bug: assertStepsRaise raise errors (all the way) when used in 'casperjs test .'
+
+ Does it run:
+ casperjs usertests.js --url='http://localhost:8080'
+ casperjs usertests.js --url='http://localhost:8080' --return-json
+ casperjs usertests.js --url='http://localhost:8080' --verbose=true --logLevel=debug
+ casperjs test test/casperjs --url='http://localhost:8080'
+ python casperjs_runner.py
+ nosetests
+ sh run_functional_tests.sh test/casperjs/
+ sh run_functional_tests.sh
+ (buildbot)
+
+ BUGS:
+ echo doesn't seem to work with python
+ trace not showing for errors here
+
+ what if:
+ does an error saving a sshot bail the entire suite?
+
+ Do the above handle:
+ test script errors
+ page errors (evaluate, find element, etc.)
+ failures
+ passes
+ python errors
+
+ Does test_runner:
+ aggregate properly (passes, failures)
+ fail on first = false
+
+ Test:
+ screenshotting
+ save html/sshots to GALAXY_TEST_SAVE (test_runner)
+
+ can we pass the entire test_env (instead of just url) from test_runner to sg?
+ support method chaining pattern
+ move selectors, text to class level (spaceghost)
+
+ modules?
+ May want to move common functions into PageObject-like subs of sg, e.g.:
+ spaceghost.loginPage.logout()
+ spaceghost.masthead.userMenu().login() // to click User -> Login
+
+ more conv. functions:
+ withMainFrame( callback )
+ getMessageInfo returns *message elementInfo or null
+
+ frames in casper are a PITA (as are steps in gen.): is there a better way to select within a frame w/o a step?
+ waitFor (with progress and finally): a gen. form of waitForHdaState
+
+*/
+// ===================================================================
+/** Extended version of casper object for use with Galaxy
+ */
+
+// ------------------------------------------------------------------- modules
+var Casper = require( 'casper' ).Casper;
+var utils = require( 'utils' );
+
+// ------------------------------------------------------------------- inheritance
+/**
+ */
+function SpaceGhost(){
+ SpaceGhost.super_.apply( this, arguments );
+ this.init.apply( this, arguments );
+}
+utils.inherits( SpaceGhost, Casper );
+
+//console.debug( 'CasperError:' + CasperError );
+
+// ------------------------------------------------------------------- error types
+PageError.prototype = new CasperError();
+PageError.prototype.constructor = CasperError;
+function PageError(){
+ CasperError.apply( this, arguments );
+ this.name = "PageError";
+}
+
+GalaxyError.prototype = new CasperError();
+GalaxyError.prototype.constructor = CasperError;
+function GalaxyError(){
+ CasperError.apply( this, arguments );
+ this.name = "GalaxyError";
+}
+
+AlertError.prototype = new CasperError();
+AlertError.prototype.constructor = CasperError;
+function AlertError(){
+ CasperError.apply( this, arguments );
+ this.name = "AlertError";
+}
+
+// =================================================================== METHODS / OVERRIDES
+// ------------------------------------------------------------------- set up
+/** More initialization: cli, event handlers, etc.
+ * @param {Object} options option hash
+ */
+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)
+ this.errors = [];
+ this.on( 'error', function( msg, backtrace ){
+ //this.debug( 'adding error to stack: ' + msg + ', trace:' + JSON.stringify( backtrace, null, 2 ) );
+ this.errors.push({ msg: msg, backtrace: backtrace });
+ });
+ this._processCLIArguments();
+ this._setUpEventHandlers();
+
+ // inject these scripts by default
+ this.debug( 'this.options.scriptDir:' + this.options.scriptDir );
+ this.options.clientScripts = [
+ this.options.scriptDir + '../../static/scripts/libs/jquery/jquery.js'
+ //...
+ ].concat( this.options.clientScripts );
+ this.debug( 'clientScripts:\n' + this.jsonStr( this.options.clientScripts ) );
+
+};
+
+/** Allow CLI arguments to set options if the proper option name is used.
+ * @example:
+ * casperjs myscript.js --verbose=true --logLevel=debug
+ */
+SpaceGhost.prototype._setOptionsFromCli = function setOptionsFromCli(){
+ // get and remove any casper options passed on the command line
+ for( var optionName in this.options ){
+ if( this.cli.has( optionName ) ){
+ //console.debug( optionName + ':' + this.options[ optionName ] + ',' + this.cli.get( optionName ) );
+ this.options[ optionName ] = this.cli.get( optionName );
+ this.cli.drop( optionName );
+ }
+ }
+};
+
+// ------------------------------------------------------------------- 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.
+ */
+SpaceGhost.prototype._processCLIArguments = function _processCLIArguments(){
+ //TODO: init these programmitically
+ var CLI_OPTIONS = {
+ returnJsonOnly : { defaultsTo: false, flag: 'return-json', help: 'send output to stderr, json to stdout' },
+ raisePageError : { defaultsTo: true, flag: 'page-error', help: 'raise errors thrown on the page' },
+ errorOnAlert : { defaultsTo: false, flag: 'error-on-alert', help: 'throw errors when a page calls alert' },
+ failOnAlert : { defaultsTo: true, flag: 'fail-on-alert', help: 'fail a test when a page calls alert' }
+ //screenOnError : { defaultsTo: false, flag: 'error-screen', help: 'capture a screenshot on a page error' },
+ //textOnError : { defaultsTo: false, flag: 'error-text', help: 'output page text on a page error' },
+ //htmlOnError : { defaultsTo: false, flag: 'error-html', help: 'output page html on a page error' }
+ };
+
+ // --url parameter required (the url of the server to test with)
+ if( !this.cli.has( 'url' ) ){
+ this.die( 'Test server URL is required - ' +
+ 'Usage: capserjs <test_script.js> --url=<test_server_url>', 1 );
+ }
+ this.baseUrl = this.cli.get( 'url' );
+
+ // --return-json: supress all output except for JSON logs, test results, and errors at finish
+ // this switch allows a testing suite to send JSON data back via stdout (w/o logs, echos interferring)
+ this.options.returnJsonOnly = CLI_OPTIONS.returnJsonOnly.defaultsTo;
+ if( this.cli.has( CLI_OPTIONS.returnJsonOnly.flag ) ){
+ this.options.returnJsonOnly = true;
+
+ //this._suppressOutput();
+ this._redirectOutputToStderr();
+
+ // output json on fail-first error
+ this.on( 'error', function( msg, backtrace ){
+ //console.debug( 'return-json caught error' );
+ if( spaceghost.options.exitOnError ){
+ this.outputStateAsJson();
+ spaceghost.exit( 1 );
+ }
+ });
+ // non-error finshes/json-output are handled in run() for now
+ }
+
+ // --error-on-alert=false: don't throw an error if the page calls alert (default: true)
+ this.options.raisePageError = CLI_OPTIONS.raisePageError.defaultsTo;
+ if( this.cli.has( CLI_OPTIONS.raisePageError.flag ) ){
+ this.options.raisePageError = this.cli.get( CLI_OPTIONS.raisePageError.flag );
+ }
+
+ // --error-on-alert=false: don't throw an error if the page calls alert (default: true)
+ this.options.errorOnAlert = CLI_OPTIONS.errorOnAlert.defaultsTo;
+ if( this.cli.has( CLI_OPTIONS.errorOnAlert.flag ) ){
+ this.options.errorOnAlert = this.cli.get( CLI_OPTIONS.errorOnAlert.flag );
+ }
+
+ // --fail-on-alert=false: don't fail a test if the page calls alert (default: true)
+ this.options.failOnAlert = CLI_OPTIONS.failOnAlert.defaultsTo;
+ if( this.cli.has( CLI_OPTIONS.failOnAlert.flag ) ){
+ this.options.failOnAlert = this.cli.get( CLI_OPTIONS.failOnAlert.flag );
+ }
+
+ /* not implemented
+ // --error-page: print the casper.debugPage (the page's text) output on an error
+ if( this.cli.has( 'error-page' ) ){
+ this.on( 'page.error', this._saveTextOnErrorHandler );
+
+ // --error-html: print the casper.debugHTML (the page's html) output on an error (mut.exc w error-text)
+ } else if( this.cli.has( 'error-html' ) ){
+ this.on( 'page.error', this._saveHtmlOnErrorHandler );
+ }
+
+ // --error-screen: print the casper.debugPage (the page's text) output on an error
+ if( this.cli.has( 'error-screen' ) ){
+ this.on( 'page.error', this._saveScreenOnErrorHandler );
+ }
+ */
+
+ // get any fixture data passed in as JSON in args
+ // (NOTE: currently the 2nd arg (with the url being 1st?)
+ this.fixtureData = ( this.cli.has( 1 ) )?( JSON.parse( this.cli.get( 1 ) ) ):( {} );
+ this.debug( 'fixtureData:' + this.jsonStr( this.fixtureData ) );
+
+};
+
+/** Suppress the normal output from the casper object (echo, errors)
+ */
+SpaceGhost.prototype._suppressOutput = function _suppressOutput(){
+ // currently (1.0) the only way to suppress test pass/fail messages
+ // (no way to re-route to log either - circular)
+ this.echo = function( msg ){};
+
+ //this.removeListener( 'error', this.listeners( 'error' )[0] );
+ // clear the casper listener that outputs formatted error messages
+ this.removeListener( 'error', this.listeners( 'error' )[1] );
+};
+
+/** Suppress the normal output from the casper object (echo, errors)
+ */
+SpaceGhost.prototype._redirectOutputToStderr = function _redirectOutputToStderr(){
+ // currently (1.0) the only way to suppress test pass/fail messages
+ // (no way to re-route to log either - circular)
+ var spaceghost = this;
+ this.echo = function( msg ){
+ spaceghost.stderr( msg );
+ };
+
+ //this.removeListener( 'error', this.listeners( 'error' )[0] );
+ // clear the casper listener that outputs formatted error messages
+ this.removeListener( 'error', this.listeners( 'error' )[1] );
+};
+
+/** Outputs logs, test results and errors in a single JSON formatted object.
+ */
+SpaceGhost.prototype.outputStateAsJson = function outputStateAsJson(){
+ var returnedJSON = {
+ logs: this.result,
+ testResults: this.test.testResults,
+ errors: this.errors
+ };
+ // use phantomjs console since echo can't be used (suppressed - see init)
+ console.debug( JSON.stringify( returnedJSON, null, 2 ) );
+};
+
+
+// ------------------------------------------------------------------- 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
+ */
+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
+ * NOTE: this has some special handling for DOM exc 12 which some casper selectors are throwing
+ * (even tho the selector still works)
+ */
+SpaceGhost.prototype._pageErrorHandler = function _pageErrorHandler( msg, backtrace ){
+ // add a page error handler to catch page errors (what we're most interested with here)
+ // normally, casper seems to let these pass unhandled
+ //console.debug( 'page.error:' + msg );
+
+ //TODO:!! lots of casper selectors are throwing this - even tho they still work
+ if( msg === 'SYNTAX_ERR: DOM Exception 12: An invalid or illegal string was specified.' ){
+ void( 0 ); // no op
+
+ } else if( this.options.raisePageError ){
+ //console.debug( '(page) Error: ' + msg );
+ //this.bypassOnError = true;
+
+ // ugh - these bounce back and forth between here and phantom.page.onError
+ // if we don't do this replace you end up with 'PageError: PageError: PageError: ...'
+ // I haven't found a great way to prevent the bouncing
+ msg = msg.replace( 'PageError: ', '' );
+ throw new PageError( msg, backtrace );
+ }
+};
+
+/** 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
+ */
+SpaceGhost.prototype._alertHandler = function _alertHandler( message ){
+ // casper info level already has outputs these
+ //this.warning( this + '(page alert)\n"' + message + '"' );
+ var ALERT_MARKER = '(page alert) ';
+
+ // either throw an error or fail the test
+ //console.debug( 'this.options.errorOnAlert: ' + this.options.errorOnAlert );
+ this.stderr( 'this.options.failOnAlert: ' + this.options.failOnAlert );
+ if( this.options.errorOnAlert ){
+ throw new PageError( ALERT_MARKER + message );
+
+ } else if( this.options.failOnAlert ){
+ //this.test.fail( ALERT_MARKER + message );
+ //this.test.fail();
+ this.test.assert( false, 'found alert message' );
+ //this.stderr( 'this.options.failOnAlert: ' + this.options.failOnAlert );
+ }
+};
+
+/** Event handler for navigation requested (loading of frames, redirects(?))
+ */
+SpaceGhost.prototype._navHandler = function _navHandler( url, navigationType, navigationLocked, isMainFrame ){
+ this.debug( 'navigation.requested: ' + url );
+};
+
+/** Set up event handlers.
+ */
+SpaceGhost.prototype._setUpEventHandlers = function _setUpEventHandlers(){
+ //console.debug( '_setUpEventHandlers' );
+
+ // ........................ page errors
+ this.on( 'page.error', this._pageErrorHandler );
+ //this.on( 'load.failed', this._loadFailedHandler );
+
+ // ........................ 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 );
+
+};
+
+// ------------------------------------------------------------------- page control
+/** An override of casper.open specifically for Galaxy.
+ * (Currently only used to change language headers)
+ */
+SpaceGhost.prototype.open = function open(){
+ //TODO: this can be moved to start (I think...?)
+ //!! override bc phantom has it's lang as 'en-US,*' and galaxy doesn't handle the '*' well (server error)
+ this.page.customHeaders = { 'Accept-Language': 'en-US' };
+ return Casper.prototype.open.apply( this, arguments );
+};
+
+/** An override to provide json output and more informative error codes
+ */
+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 );
+
+ // if --return-json is used: output json and exit
+ if( this.options.returnJsonOnly ){
+ this.outputStateAsJson();
+ this.exit( returnCode );
+
+ // otherwise, render the nice casper output and exit
+ } else {
+ this.test.renderResults( true, returnCode );
+ }
+ };
+ Casper.prototype.run.call( this, new_onComplete, time );
+};
+
+/** Install a function as an error handler temporarily, run a function with steps, then remove the handler.
+ * A rough stand-in for try catch with steps.
+ * CatchFn will be passed error's msg and trace.
+ * @param {Function} stepsFn a function that puts casper steps on the stack (then, thenOpen, etc.)
+ * @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,
+ errorMsg = '', errorTrace = [],
+ recordError = function( msg, trace ){
+ errorMsg = msg; errorTrace = trace;
+ };
+
+ // dont bail on the error (but preserve option), install hndlr to simply record msg, trace
+ //NOTE: haven't had to remove other listeners yet
+ this.then( function(){
+ originalExitOnError = this.options.exitOnError;
+ this.options.exitOnError = false;
+ this.on( 'error', recordError );
+ });
+
+ // try the step...
+ this.then( stepsFn );
+
+ this.then( function(){
+ // ...and if an error was recorded call the catch with the info
+ if( errorMsg ){
+ catchFn.call( this, errorMsg, errorTrace );
+ }
+ // remove that listener either way and restore the bail option
+ this.removeListener( 'error', recordError );
+ this.options.exitOnError = originalExitOnError;
+ });
+};
+
+
+// =================================================================== TESTING
+//TODO: form fill doesn't work as casperjs would want it - often a button -> controller url
+//TODO: saveScreenshot (to GALAXY_TEST_SAVE)
+//TODO: saveHtml (to GALAXY_TEST_SAVE)
+
+/** 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
+ * @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)
+ */
+SpaceGhost.prototype.assertSelectorAndTextInFrame = function assertSelectorAndTextInFrame( selector, text, frame ){
+ var spaceghost = this;
+ function assertSelectorAndText( selector, text ){
+ spaceghost.test.assertExists( selector,
+ format( "found '%s' in %s", selector, frame ) );
+ spaceghost.test.assertSelectorHasText( selector, text,
+ format( "%s contains '%s'", selector, text ) );
+ }
+ if( frame ){
+ this.withFrame( frame, function(){
+ assertSelectorAndText( selector, text );
+ });
+ } else {
+ assertSelectorAndText( selector, text );
+ }
+}
+
+/** 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')
+ */
+SpaceGhost.prototype.assertErrorMessage = function assertSelectorAndTextInFrame( message, frame, messageSelector ){
+ messageSelector = messageSelector || this.selectors.messages.error;
+ frame = frame || this.selectors.frames.main;
+ this.assertSelectorAndTextInFrame( messageSelector, message, frame );
+};
+
+/** Assert that stepsFn (which contains casper.then or some other casper step function) raises an error with
+ * a msg that contains some text (msgContains).
+ * @param {String} msgContains some portion of the correct error msg
+ * @param {Function} stepsFn a function that puts casper steps on the stack (then, thenOpen, etc.)
+ */
+SpaceGhost.prototype.assertStepsRaise = function assertStepsRaise( msgContains, stepsFn, removeOtherListeners ){
+ // casper provides an assertRaises but this doesn't work well with steps
+ //TODO: * @param {Boolean} removeOtherListeners option to remove other listeners while this fires
+ var spaceghost = this;
+ function testTheError( msg, backtrace ){
+ spaceghost.test.assert( msg.indexOf( msgContains ) != -1, 'Raised correct error: ' + msg );
+ }
+ this.tryStepsCatch( stepsFn, testTheError );
+};
+
+// =================================================================== 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
+ */
+SpaceGhost.prototype.elementInfoOrNull = function elementInfoOrNull( selector ){
+ var found = null;
+ try {
+ found = this.getElementInfo( selector );
+ } catch( err ){}
+ return found;
+};
+
+/** Wraps casper.click in try, returning true if element found and clicked, false if not instead of erroring.
+ * @param {String} selector css or xpath selector for the element to find
+ */
+SpaceGhost.prototype.tryClick = function tryClick( selector ){
+ var done = false;
+ try {
+ found = this.click( selector );
+ done = true;
+ } catch( err ){}
+ return done;
+};
+
+
+// =================================================================== GALAXY CONVENIENCE
+/** Gets a psuedo-random (unique?) email based on the time stamp.
+ * Helpful for testing registration.
+ * @param {String} username email user (defaults to 'test')
+ * @param {String} domain email domain (defaults to 'test.test')
+ */
+SpaceGhost.prototype.getRandomEmail = function getRandomEmail( username, domain ){
+ username = username || 'test';
+ domain = domain || 'test.test';
+ return username + Date.now() + '@' + domain;
+};
+
+
+/** Tests registering a new user on the Galaxy instance by submitting the registration form.
+ * NOTE: this version does NOT throw an error on a bad registration.
+ * It is meant for testing the registration functionality and, therefore, is marked as private.
+ * Other tests should use registerUser
+ * @param {String} email the users email address
+ * @param {String} password the users password
+ * @param {String} username the users ...username! (optional: will use 1st part of email)
+ * @param {String} confirm password confirmation (optional: defaults to password)
+ */
+SpaceGhost.prototype._submitUserRegistration = function _submitUserRegistration( email, password, username, confirm ){
+ var userInfo = {
+ email : email,
+ password: password,
+ // default username to first part of email
+ username:( !username && email.match( /^\w*/ ) )?( email.match( /^\w*/ ) ):( username ),
+ // default confirm duplicate of password
+ confirm : ( confirm !== undefined )?( confirm ):( password )
+ };
+ this.debug( 'registering user:\n' + this.jsonStr( userInfo ) );
+
+ this.thenOpen( this.baseUrl, function(){
+
+ this.clickLabel( this.labels.masthead.menus.user );
+ this.clickLabel( this.labels.masthead.userMenu.register );
+
+ this.withFrame( this.selectors.frames.main, function mainBeforeRegister(){
+ this.debug( 'submitting registration... ' + this.getCurrentUrl() );
+ this.fill( this.selectors.registrationPage.form, userInfo, false );
+ // need manual up
+ this.click( xpath( this.selectors.registrationPage.submit_xpath ) );
+ });
+
+ this.withFrame( this.selectors.frames.main, function mainAfterRegister(){
+ var messageInfo = this.getElementInfo( this.selectors.messages.all );
+ this.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) );
+ });
+ });
+};
+
+/** Register a new user on the Galaxy instance.
+ * @param {String} email the users email address
+ * @param {String} password the users password
+ * @param {String} username the users ...username! (optional: will use 1st part of email)
+ */
+SpaceGhost.prototype.registerUser = function registerUser( email, password, username ){
+ this._submitUserRegistration( email, password, username );
+ this.then( function(){
+ this.withFrame( this.selectors.frames.main, function mainAfterRegister(){
+ var messageInfo = this.getElementInfo( this.selectors.messages.all );
+ this.debug( 'post registration message:\n' + this.jsonStr( messageInfo ) );
+
+ if( messageInfo.attributes[ 'class' ] === 'errormessage' ){
+ throw new GalaxyError( 'RegistrationError: ' + messageInfo.html );
+ }
+ });
+ });
+ return this;
+};
+
+/** Log out the current user
+ */
+SpaceGhost.prototype.logout = function logoutUser(){
+ this.clickLabel( this.labels.masthead.menus.user );
+ this.clickLabel( this.labels.masthead.userMenu.login );
+ this.thenOpen( this.baseUrl, function(){
+ //TODO: handle already logged out
+ this.clickLabel( this.labels.masthead.menus.user );
+ this.clickLabel( this.labels.masthead.userMenu.logout );
+ });
+};
+
+/** Tests logging in a user on the Galaxy instance by submitting the login form.
+ * NOTE: this version does NOT throw an error on a bad login.
+ * It is meant for testing the login functionality and, therefore, is marked as private.
+ * Other tests should use login
+ * @param {String} email the users email address
+ * @param {String} password the users password
+ */
+SpaceGhost.prototype._submitLogin = function logoutUser( email, password ){
+ var loginInfo = {
+ //NOTE: keys are used as name selectors in the fill fn - must match the names of the inputs
+ email: email,
+ password: password
+ };
+
+ this.thenOpen( this.baseUrl, function(){
+
+ this.clickLabel( this.labels.masthead.menus.user );
+ this.clickLabel( this.labels.masthead.userMenu.login );
+
+ this.withFrame( this.selectors.frames.main, function mainBeforeLogin(){
+ this.debug( '(' + this.getCurrentUrl() + ') logging in user:\n' + this.jsonStr( loginInfo ) );
+ this.fill( this.selectors.loginPage.form, loginInfo, false );
+ this.click( xpath( this.selectors.loginPage.submit_xpath ) );
+ });
+ this.withFrame( this.selectors.frames.main, function mainAfterLogin(){
+ //TODO: prob. could use a more generalized form of this for url breakdown/checking
+ if( this.getCurrentUrl().search( this.selectors.loginPage.url_regex ) != -1 ){
+ var messageInfo = this.getElementInfo( this.selectors.messages.all );
+ this.debug( 'post login message:\n' + this.jsonStr( messageInfo ) );
+ }
+ });
+ });
+};
+
+/** Logs in a user. Throws error on bad log in.
+ * @param {String} email the users email address
+ * @param {String} password the users password
+ */
+SpaceGhost.prototype.login = function login( email, password ){
+ this._submitLogin( email, password );
+ this.then( function(){
+ this.withFrame( this.selectors.frames.main, function mainAfterLogin(){
+ if( this.getCurrentUrl().search( this.selectors.loginPage.url_regex ) != -1 ){
+ var messageInfo = this.getElementInfo( this.selectors.messages.all );
+ if( messageInfo && messageInfo.attributes[ 'class' ] === 'errormessage' ){
+ throw new GalaxyError( 'LoginError: ' + messageInfo.html );
+ }
+ }
+ });
+ if( this.loggedInAs() === email ){
+ this.debug( 'logged in as ' + email );
+ }
+ });
+ return this;
+};
+
+/** Fetch the email of the currently logged in user (or '' if not logged in)
+ * @returns {String} email of currently logged in user or '' if no one logged in
+ */
+SpaceGhost.prototype.loggedInAs = function loggedInAs(){
+ var userEmail = '';
+ try {
+ var loggedInInfo = this.getElementInfo( xpath( this.selectors.masthead.userMenu.userEmail_xpath ) );
+ userEmail = loggedInInfo.text;
+ } catch( err ){
+ this.error( err );
+ }
+ //console.debug( 'loggedInInfo:', this.jsonStr( loggedInInfo ) );
+ return userEmail;
+};
+
+/** Attempts to login a user - if that raises an error (LoginError), register the user
+ * @param {String} email the users email address
+ * @param {String} password the users password
+ * @param {String} username the users ...username! (optional: will use 1st part of email)
+ */
+SpaceGhost.prototype.loginOrRegisterUser = function loginOrRegisterUser( email, password, username ){
+ // attempt a login, if that fails - register
+ this.tryStepsCatch( function tryToLogin(){
+ this.open( this.baseUrl ).login( email, password );
+
+ }, function failedLoginRegister(){
+ this.open( this.baseUrl ).registerUser( email, password, username );
+ });
+ return this;
+};
+
+/** Tests uploading a file.
+ * NOTE: this version does NOT throw an error on a bad upload.
+ * It is meant for testing the upload functionality and, therefore, is marked as private.
+ * Other tests should use uploadFile
+ * @param {String} filepath the local filesystem path of the file to upload (absolute (?))
+ */
+SpaceGhost.prototype._uploadFile = function _uploadFile( filepath ){
+ var uploadInfo = {};
+ //TODO: check file exists using phantom.fs
+ //TODO: pull from test data
+ uploadInfo[ this.tools.upload.fileInput ] = filepath;
+ this.debug( 'uploading file: ' + filepath );
+
+ spaceghost.then( function(){
+ spaceghost.withFrame( this.selectors.frames.tools, function(){
+ this.clickLabel( this.tools.upload.panelLabel );
+ });
+ });
+
+ this.then( function beginUpload(){
+ spaceghost.withFrame( this.selectors.frames.main, function(){
+ this.fill( this.tools.general.form, uploadInfo, false );
+
+ // the following throws:
+ // [error] [remote] Failed dispatching clickmouse event on xpath selector: //input[@value="Execute"]:
+ // PageError: TypeError: 'undefined' is not a function (evaluating '$(this).formSerialize()')
+
+ // ...and yet the upload still seems to work
+ this.click( xpath( this.tools.general.executeButton_xpath ) );
+ });
+ });
+ this.withFrame( this.selectors.frames.main, function afterUpload(){
+ var messageInfo = this.elementInfoOrNull( this.selectors.messages.all );
+ this.debug( 'post upload message:\n' + this.jsonStr( messageInfo ) );
+ });
+};
+
+/** Uploads a file.
+ * @param {String} filepath the local filesystem path of the file to upload (absolute (?))
+ */
+SpaceGhost.prototype.uploadFile = function uploadFile( filepath ){
+ this._uploadFile( filepath );
+ this.then( function(){
+ this.withFrame( this.selectors.frames.main, function mainAfterUpload(){
+ var messageInfo = this.elementInfoOrNull( this.selectors.messages.all );
+ if( ( !messageInfo )
+ || ( messageInfo.attributes[ 'class' ] !== 'donemessagelarge' )
+ || ( messageInfo.text.indexOf( this.text.upload.success ) === -1 ) ){
+ throw new GalaxyError( 'UploadError: ' + this.jsonStr( messageInfo ) );
+ }
+ });
+ });
+ return this;
+};
+
+/** Parses the hid and name of a newly uploaded file from the tool execution donemessagelarge
+ * @param {String} doneMsgText the text extracted from the donemessagelarge after a tool execution
+ */
+SpaceGhost.prototype.parseDoneMessageForTool = function parseDoneMessageForTool( doneMsgText ){
+ //TODO: test on non-upload
+ var executionInfo = {};
+ var textMatch = doneMsgText.match( /added to the queue:\n\n(\d+)\: (.*)\n/m );
+ if( textMatch ){
+ if( textMatch.length > 1 ){
+ executionInfo.hid = parseInt( textMatch[1], 10 );
+ }
+ if( textMatch.length > 2 ){
+ executionInfo.name = textMatch[2];
+ }
+ executionInfo.name = textMatch[2];
+ }
+ return executionInfo;
+};
+
+/** Find the casper element info of the hda wrapper given the hda title and hid.
+ * NOTE: if more than one is found, will return the first found.
+ * precondition: you should wrap this with withFrame( 'galaxy_history' ) :(
+ * @param {String} title the title of the hda
+ * @param {Int} hid (optional) the hid of the hda to look for
+ * @returns {Object|null} ElementInfo of the historyItemWrapper found, null if not found
+ */
+SpaceGhost.prototype.hdaElementInfoByTitle = function hdaElementInfoByTitle( title, hid ){
+ var spaceghost = this,
+ titleContains = ( hid !== undefined )?( hid + ': ' + title ):( title ),
+ wrapperInfo = null;
+
+ wrapperInfo = spaceghost.evaluate( function( titleContains ){
+ // find the title, then the wrapper (2 containers up)
+ var $title = $( '.historyItemTitle:contains(' + titleContains + ')' );
+ var $wrapper = $title.parent().parent();
+ return (( $wrapper.attr( 'id' ) )?( __utils__.getElementInfo( '#' + $wrapper.attr( 'id' ) )):( null ));
+ }, titleContains );
+
+ return wrapperInfo;
+};
+
+/** Wait for the hda with given id to move into the given state.
+ * @param {String} hdaSelector selector for hda (should be historyItemWrapper)
+ * @param {String} finalState hda state to wait for (e.g. 'ok', 'error', 'running', 'queued', etc.)
+ * @param {Function} whenInStateFn called when hda goes into finalState
+ * @param {Function} timeoutFn called when maxWaitMs have passed without the desired state
+ * @param {Int} maxWaitMs number of milliseconds to wait before timing out (defaults to options.waitTimeout)
+ */
+SpaceGhost.prototype.waitForHdaState = function waitForHdaState( hdaSelector, finalState,
+ whenInStateFn, timeoutFn, maxWaitMs ){
+ //TODO:?? explicitly a historyWrapper id?
+ maxWaitMs = maxWaitMs || this.options.waitTimeout;
+ var finalStateClass = '.historyItem-' + finalState;
+
+ this.then( function(){
+ this.withFrame( this.selectors.frames.history, function(){
+ // wait for state, preferrably debugging intermediate states
+ var spaceghost = this,
+
+ // we need a larger timeout for these - it can take a bit
+ oldWaitTimeout = this.options.waitTimeout,
+
+ // output some progress indicator within the test (debug)
+ progressIntervalId = setInterval( function progress(){
+ var state = spaceghost.evaluate( function( hdaSelector ){
+ var $wrapperClasses = $( hdaSelector ).attr( 'class' );
+ return $wrapperClasses.match( /historyItem\-(\w+)/ )[1];
+ }, hdaSelector );
+ spaceghost.debug( hdaSelector + ': ' + state );
+ }, 1000 ),
+
+ // when done, close down the progress reporter and reset the wait timeout to what it was
+ finallyFn = function(){
+ spaceghost.options.waitTimeout = oldWaitTimeout;
+ clearInterval( progressIntervalId );
+ };
+
+ this.options.waitTimeout = maxWaitMs;
+ this.waitForSelector( hdaSelector + finalStateClass, function _whenInState(){
+ this.debug( 'HDA now in state ' + finalState + ':\n'
+ + this.jsonStr( this.elementInfoOrNull( hdaSelector ) ) );
+ whenInStateFn.call( this );
+ finallyFn();
+
+ }, function timeout(){
+ this.debug( 'timed out:\n'
+ + this.jsonStr( this.elementInfoOrNull( hdaSelector ) ) );
+ timeoutFn.call( this );
+ finallyFn();
+ }
+ );
+ });
+ });
+};
+
+// =================================================================== MISCELAIN
+/** Send message to stderr
+ */
+SpaceGhost.prototype.stderr = function( msg ){
+ var fs = require( 'fs' );
+ fs.write( '/dev/stderr', msg + '\n', 'w' );
+};
+
+// convenience logging funcs
+/** log using level = 'debug' and default namespace = 'spaceghost'
+ */
+SpaceGhost.prototype.debug = function( msg, namespace ){
+ namespace = namespace || 'spaceghost';
+ this.log( msg, 'debug', namespace );
+};
+
+/** log using level = 'info' and default namespace = 'spaceghost'
+ */
+SpaceGhost.prototype.info = function( msg, namespace ){
+ namespace = namespace || 'spaceghost';
+ this.log( msg, 'info', namespace );
+};
+
+/** log using level = 'info' 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'
+ */
+SpaceGhost.prototype.error = function( msg, namespace ){
+ namespace = namespace || 'spaceghost';
+ this.log( msg, 'error', namespace );
+};
+
+/** log despite logLevel settings, unless returnJsonOnly is set
+ */
+SpaceGhost.prototype.out = function( msg, namespace ){
+ if( !this.options.returnJsonOnly ){
+ console.debug( msg );
+ }
+};
+
+/** JSON formatter
+ */
+SpaceGhost.prototype.jsonStr = function( obj ){
+ return JSON.stringify( obj, null, 2 );
+};
+
+/** Debug SG itself
+ */
+SpaceGhost.prototype.debugMe = function(){
+ console.debug( 'options:\n' + this.jsonStr( this.options ) );
+ console.debug( 'cli:\n' + this.jsonStr( this.cli ) );
+};
+
+/** Get the last error on the stack.
+ */
+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(){
+ var currentUrl = '';
+ try {
+ currentUrl = this.getCurrentUrl();
+ } catch( err ){}
+ return 'SpaceGhost(' + currentUrl + ')';
+};
+
+
+// =================================================================== TEST DATA
+// maintain selectors, labels, text here in one central location
+
+//TODO: to separate file?
+SpaceGhost.prototype.selectors = {
+ 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'
+ },
+ loginPage : {
+ form : 'form#login',
+ submit_xpath : "//input[@value='Login']",
+ url_regex : /\/user\/login/
+ },
+ registrationPage : {
+ form : 'form#registration',
+ submit_xpath : "//input[@value='Submit']"
+ }
+};
+
+SpaceGhost.prototype.labels = {
+ masthead : {
+ menus : {
+ user : 'User'
+ },
+ userMenu : {
+ register : 'Register',
+ login : 'Login',
+ logout : 'Logout'
+ }
+ }
+};
+
+SpaceGhost.prototype.tools = {
+ general : {
+ form : 'form#tool_form',
+ executeButton_xpath : '//input[@value="Execute"]'
+ },
+ upload : {
+ panelLabel : 'Upload File',
+ fileInput : 'files_0|file_data' // is this general?
+ }
+};
+
+SpaceGhost.prototype.text = {
+ registrationPage : {
+ badEmailError : 'Enter a real email address'
+ //...
+ },
+ upload : {
+ success : 'The following job has been successfully added to the queue'
+ }
+};
+
+// =================================================================== EXPORTS
+/**
+ */
+exports.SpaceGhost = SpaceGhost;
+exports.PageError = PageError;
+exports.GalaxyError = GalaxyError;
+exports.AlertError = AlertError;
+/**
+ */
+exports.create = function create(options) {
+ "use strict";
+ return new SpaceGhost(options);
+};
+
+// ------------------------------------------------------------------- included libs
+//??: can we require underscore, etc. from the ../../static/scripts/lib?
+// yep!
+//var _ = require( '../../static/scripts/libs/underscore' );
+//var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
+//console.debug( JSON.stringify( _.pluck(stooges, 'name') ) );
+//exports._ = _;
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/upload-tests.js
--- /dev/null
+++ b/test/casperjs/upload-tests.js
@@ -0,0 +1,132 @@
+// 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:
+
+ find a way to error on bad upload?
+ general tool execution
+*/
+// =================================================================== globals and helpers
+var email = spaceghost.getRandomEmail(),
+ password = '123456';
+if( spaceghost.fixtureData.testUser ){
+ email = spaceghost.fixtureData.testUser.email;
+ password = spaceghost.fixtureData.testUser.password;
+}
+
+
+// =================================================================== TESTS
+// ------------------------------------------------------------------- start a new user
+spaceghost.loginOrRegisterUser( email, password );
+//??: why is a reload needed here? If we don't, loggedInAs === '' ...
+spaceghost.thenOpen( spaceghost.baseUrl, function(){
+ var loggedInAs = spaceghost.loggedInAs();
+ this.test.assert( loggedInAs === email, 'loggedInAs() matches email: "' + loggedInAs + '"' );
+});
+
+// ------------------------------------------------------------------- get avail. tools
+// list available tools
+//spaceghost.then( function(){
+// spaceghost.withFrame( 'galaxy_tools', function(){
+// //var availableTools = this.fetchText( 'a.tool-link' );
+//
+// var availableTools = this.evaluate( function(){
+// //var toolTitles = __utils__.findAll( 'div.toolTitle' );
+// //return Array.prototype.map.call( toolTitles, function( e ){
+// // //return e.innerHtml;
+// // return e.textContent || e.innerText;
+// //}).join( '\n' );
+//
+// var toolLinks = __utils__.findAll( 'a.tool-link' );
+// return Array.prototype.map.call( toolLinks, function( e ){
+// //return e.innerHtml;
+// return e.textContent || e.innerText;
+// }).join( '\n' );
+// });
+// this.debug( 'availableTools: ' + availableTools );
+// });
+//});
+
+// ------------------------------------------------------------------- upload from fs
+// test uploading from the filesystem
+var uploadInfo = {};
+spaceghost.then( function(){
+ // strangely, this works with a non-existant file --> empty txt file
+ var filename = '1.sam';
+ var filepath = this.options.scriptDir + '/../../test-data/' + filename;
+ this._uploadFile( filepath );
+
+ // when an upload begins successfully...
+ // 1. main should reload with a donemessagelarge
+ // 2. which contains the uploaded file's new hid
+ // 3. and the filename of the upload
+ this.withFrame( 'galaxy_main', function(){
+ var doneElementInfo = this.elementInfoOrNull( '.donemessagelarge' );
+ this.test.assert( doneElementInfo !== null,
+ "Found donemessagelarge after uploading file" );
+
+ uploadInfo = this.parseDoneMessageForTool( doneElementInfo.text );
+ this.test.assert( uploadInfo.hid >= 0,
+ 'Found sensible hid from upload donemessagelarge: ' + uploadInfo.hid );
+ this.test.assert( uploadInfo.name === filename,
+ 'Found matching name from upload donemessagelarge: ' + uploadInfo.name );
+ });
+
+});
+
+// wait for upload to finish
+spaceghost.then( function(){
+ var hdaInfo = null;
+
+ this.withFrame( 'galaxy_history', function(){
+ hdaInfo = this.hdaElementInfoByTitle( uploadInfo.name, uploadInfo.hid );
+ this.debug( 'hda:\n' + this.jsonStr( hdaInfo ) );
+ });
+
+ this.then( function(){
+ this.test.comment( 'Waiting for upload to move to ok state in history' );
+ //precondition: needs class
+ var hdaStateClass = hdaInfo.attributes[ 'class' ].match( /historyItem\-(\w+)/ )[0];
+ if( hdaStateClass !== 'historyItem-ok' ){
+ this.waitForHdaState( '#' + hdaInfo.attributes.id, 'ok',
+ function whenInStateFn(){
+ this.test.assert( true, 'Upload completed successfully for: ' + uploadInfo.name );
+
+ }, function timeoutFn(){
+ this.test.fail( 'Test timedout for upload: ' + uploadInfo.name );
+
+ // wait a maximum of 30 secs
+ }, 30 * 1000 );
+ }
+ });
+});
+
+// ===================================================================
+spaceghost.run( function(){
+ this.test.done();
+});
diff -r ba8c49884f7daab5df8b62bd631157058c7ee910 -r 01e73b11a46f87b03af29581603378b06187051d test/casperjs/utils/simple-galaxy.js
--- /dev/null
+++ b/test/casperjs/utils/simple-galaxy.js
@@ -0,0 +1,1 @@
+/Users/carleberhard/explore/phantom-casper/simple-galaxy.js
\ No newline at end of file
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.
1
0
commit/galaxy-central: carlfeberhard: Fix to trackster/util.js exports; Fix to docstring in history_contents; Fix alert text in hda-model
by Bitbucket 12 Feb '13
by Bitbucket 12 Feb '13
12 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ba8c49884f7d/
changeset: ba8c49884f7d
user: carlfeberhard
date: 2013-02-12 20:26:30
summary: Fix to trackster/util.js exports; Fix to docstring in history_contents; Fix alert text in hda-model
affected #: 5 files
diff -r d2e30720ba78cbee8b6a6a63daa91702778040bc -r ba8c49884f7daab5df8b62bd631157058c7ee910 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
@@ -113,11 +113,6 @@
"""
Returns a dictionary for an HDA that raised an exception when it's
dictionary was being built.
- {
- 'id' : < the encoded dataset id >,
- 'type' : < name of the dataset >,
- 'url' : < api url to retrieve this datasets full data >,
- }
"""
return {
'id' : hda_id,
diff -r d2e30720ba78cbee8b6a6a63daa91702778040bc -r ba8c49884f7daab5df8b62bd631157058c7ee910 static/scripts/mvc/history/history-model.js
--- a/static/scripts/mvc/history/history-model.js
+++ b/static/scripts/mvc/history/history-model.js
@@ -191,7 +191,7 @@
// if not interruption by iframe reload
//TODO: remove when iframes are removed
if( !( ( xhr.readyState === 0 ) && ( xhr.status === 0 ) ) ){
- alert( _l( 'Error getting history updates from the server.' ) + '\n' + error );
+ alert( _l( 'Error getting history updates from the server:' ) + '\n' + error );
history.log( 'stateUpdater error:', error, 'responseText:', xhr.responseText );
}
});
diff -r d2e30720ba78cbee8b6a6a63daa91702778040bc -r ba8c49884f7daab5df8b62bd631157058c7ee910 static/scripts/packed/mvc/history/history-model.js
--- a/static/scripts/packed/mvc/history/history-model.js
+++ b/static/scripts/packed/mvc/history/history-model.js
@@ -1,1 +1,1 @@
-var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",diskSize:0,deleted:false,annotation:null,message:null},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b){if(_.isArray(b)){this.hdas.reset(b);this.checkForUpdates()}else{if(_.isString(b)&&(b.match(/error/i))){alert(_l("Error loading bootstrapped history")+":\n"+b)}}}},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.trigger("loaded:user",e[0]);b.trigger("loaded",d[0])}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.hdaIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();b.trigger("loaded:hdas",d);if(c){callback(b)}})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(f,d,e){if(!((f.readyState===0)&&(f.status===0))){alert(_l("Error getting history updates from the server.")+"\n"+e);c.log("stateUpdater error:",e,"responseText:",f.responseText)}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{var g=_l("ERROR updating hdas from api history contents")+": ";a.log(g,b,h,c,d,f);alert(g+b.join(","))}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
+var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",diskSize:0,deleted:false,annotation:null,message:null},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b){if(_.isArray(b)){this.hdas.reset(b);this.checkForUpdates()}else{if(_.isString(b)&&(b.match(/error/i))){alert(_l("Error loading bootstrapped history")+":\n"+b)}}}},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.trigger("loaded:user",e[0]);b.trigger("loaded",d[0])}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.hdaIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();b.trigger("loaded:hdas",d);if(c){callback(b)}})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(f,d,e){if(!((f.readyState===0)&&(f.status===0))){alert(_l("Error getting history updates from the server:")+"\n"+e);c.log("stateUpdater error:",e,"responseText:",f.responseText)}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{var g=_l("ERROR updating hdas from api history contents")+": ";a.log(g,b,h,c,d,f);alert(g+b.join(","))}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
diff -r d2e30720ba78cbee8b6a6a63daa91702778040bc -r ba8c49884f7daab5df8b62bd631157058c7ee910 static/scripts/packed/viz/trackster/util.js
--- a/static/scripts/packed/viz/trackster/util.js
+++ b/static/scripts/packed/viz/trackster/util.js
@@ -1,1 +1,1 @@
-define(function(){exports={};exports.ServerStateDeferred=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(a){return true}},go:function(){var d=$.Deferred(),c=this,f=c.get("ajax_settings"),e=c.get("success_fn"),b=c.get("interval"),a=function(){$.ajax(f).success(function(g){if(e(g)){d.resolve(g)}else{setTimeout(a,b)}})};a();return d}});exports.get_random_color=function(a){if(!a){a="#ffffff"}if(typeof(a)==="string"){a=[a]}for(var j=0;j<a.length;j++){a[j]=parseInt(a[j].slice(1),16)}var n=function(t,s,i){return((t*299)+(s*587)+(i*114))/1000};var e=function(v,u,w,s,i,t){return(Math.max(v,s)-Math.min(v,s))+(Math.max(u,i)-Math.min(u,i))+(Math.max(w,t)-Math.min(w,t))};var g,o,f,k,q,h,r,c,d,b,p,m=false,l=0;do{g=Math.round(Math.random()*16777215);o=(g&16711680)>>16;f=(g&65280)>>8;k=g&255;d=n(o,f,k);m=true;for(j=0;j<a.length;j++){q=a[j];h=(q&16711680)>>16;r=(q&65280)>>8;c=q&255;b=n(h,r,c);p=e(o,f,k,h,r,c);if((Math.abs(d-b)<40)||(p<200)){m=false;break}}l++}while(!m&&l<=10);return"#"+(16777216+g).toString(16).substr(1,6)};return exports});
\ No newline at end of file
+define(function(){var b=Backbone.Model.extend({defaults:{ajax_settings:{},interval:1000,success_fn:function(c){return true}},go:function(){var f=$.Deferred(),e=this,h=e.get("ajax_settings"),g=e.get("success_fn"),d=e.get("interval"),c=function(){$.ajax(h).success(function(i){if(g(i)){f.resolve(i)}else{setTimeout(c,d)}})};c();return f}});var a=function(c){if(!c){c="#ffffff"}if(typeof(c)==="string"){c=[c]}for(var l=0;l<c.length;l++){c[l]=parseInt(c[l].slice(1),16)}var p=function(v,u,i){return((v*299)+(u*587)+(i*114))/1000};var g=function(x,w,y,u,i,v){return(Math.max(x,u)-Math.min(x,u))+(Math.max(w,i)-Math.min(w,i))+(Math.max(y,v)-Math.min(y,v))};var j,q,h,m,s,k,t,e,f,d,r,o=false,n=0;do{j=Math.round(Math.random()*16777215);q=(j&16711680)>>16;h=(j&65280)>>8;m=j&255;f=p(q,h,m);o=true;for(l=0;l<c.length;l++){s=c[l];k=(s&16711680)>>16;t=(s&65280)>>8;e=s&255;d=p(k,t,e);r=g(q,h,m,k,t,e);if((Math.abs(f-d)<40)||(r<200)){o=false;break}}n++}while(!o&&n<=10);return"#"+(16777216+j).toString(16).substr(1,6)};return{ServerStateDeferred:b,get_random_color:a}});
\ No newline at end of file
diff -r d2e30720ba78cbee8b6a6a63daa91702778040bc -r ba8c49884f7daab5df8b62bd631157058c7ee910 static/scripts/viz/trackster/util.js
--- a/static/scripts/viz/trackster/util.js
+++ b/static/scripts/viz/trackster/util.js
@@ -1,12 +1,10 @@
define(function(){
-exports = {};
-
/**
* Implementation of a server-state based deferred. Server is repeatedly polled, and when
* condition is met, deferred is resolved.
*/
-exports.ServerStateDeferred = Backbone.Model.extend({
+var ServerStateDeferred = Backbone.Model.extend({
defaults: {
ajax_settings: {},
interval: 1000,
@@ -44,7 +42,7 @@
* or set of colors.
* @param colors a color or list of colors in the format '#RRGGBB'
*/
-exports.get_random_color = function(colors) {
+var get_random_color = function(colors) {
// Default for colors is white.
if (!colors) { colors = "#ffffff"; }
@@ -110,6 +108,9 @@
return '#' + ( 0x1000000 + new_color ).toString(16).substr(1,6);
};
-return exports;
+return {
+ ServerStateDeferred : ServerStateDeferred,
+ get_random_color : get_random_color
+};
-})
\ No newline at end of file
+});
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.
1
0
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/d2e30720ba78/
changeset: d2e30720ba78
user: jgoecks
date: 2013-02-12 18:01:37
summary: Pack scripts.
affected #: 2 files
diff -r a6fe104c109feea61995567336a1aaf515acf0d5 -r d2e30720ba78cbee8b6a6a63daa91702778040bc static/scripts/packed/mvc/data.js
--- a/static/scripts/packed/mvc/data.js
+++ b/static/scripts/packed/mvc/data.js
@@ -1,1 +1,1 @@
-define(["libs/backbone/backbone-relational"],function(){var b=Backbone.RelationalModel.extend({});var c=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){var f=new b();_.each(_.keys(this.attributes),function(g){if(g.indexOf("metadata_")===0){var h=g.split("metadata_")[1];f.set(h,this.attributes[g]);delete this.attributes[g]}},this);this.set("metadata",f)},get_metadata:function(f){return this.attributes.metadata.get(f)},urlRoot:galaxy_paths.get("datasets_url")});var a=c.extend({defaults:_.extend({},c.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(f){c.prototype.initialize.call(this);chunk_index=(this.attributes.first_data_chunk?1:0)},set_first_chunk:function(f){this.attributes.first_data_chunk=f;this.attributes.chunk_index=1},get_next_chunk:function(){if(this.attributes.at_eof){return null}var f=this,g=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:f.attributes.chunk_index++}).success(function(h){var i;if(h.ck_data!==""){i=h}else{f.attributes.at_eof=true;i=null}g.resolve(i)});return g}});var e=Backbone.Collection.extend({model:c});var d=Backbone.View.extend({initialize:function(f){},render:function(){this.$el.append($("<div/>").attr("id","loading_indicator"));var i=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(i);var f=this.model.get_metadata("column_names");if(f){i.append("<tr><th>"+f.join("</th><th>")+"</th></tr>")}var h=this.model.get("first_data_chunk");if(h){this._renderChunk(h)}var g=this;$(window).scroll(function(){if($(window).scrollTop()===$(document).height()-$(window).height()){$.when(g.model.get_next_chunk()).then(function(j){if(j){g._renderChunk(j)}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},_renderCell:function(h,f,i){var g=this.model.get_metadata("column_types");if(i!==undefined){return $("<td>").attr("colspan",i).addClass("stringalign").text(h)}else{if(g[f]==="str"||g==="list"){return $("<td>").addClass("stringalign").text(h)}else{return $("<td>").text(h)}}},_renderRow:function(f){var g=f.split("\t"),i=$("<tr>"),h=this.model.get_metadata("columns");if(g.length===h){_.each(g,function(k,j){i.append(this._renderCell(k,j))},this)}else{if(g.length>h){_.each(g.slice(0,h-1),function(k,j){i.append(this._renderCell(k,j))},this);i.append(this._renderCell(g.slice(h-1).join("\t"),h-1))}else{if(h>5&&g.length===h-1){_.each(g,function(k,j){i.append(this._renderCell(k,j))},this);i.append($("<td>"))}else{i.append(this._renderCell(f,0,h))}}}return i},_renderChunk:function(f){var g=this.$el.find("table");_.each(f.ck_data.split("\n"),function(h,i){g.append(this._renderRow(h))},this)}});return{Dataset:c,TabularDataset:a,DatasetCollection:e,TabularDatasetChunkedView:d}});
\ No newline at end of file
+define(["libs/backbone/backbone-relational"],function(){var c=Backbone.RelationalModel.extend({});var d=Backbone.RelationalModel.extend({defaults:{id:"",type:"",name:"",hda_ldda:"hda",metadata:null},initialize:function(){var h=new c();_.each(_.keys(this.attributes),function(i){if(i.indexOf("metadata_")===0){var j=i.split("metadata_")[1];h.set(j,this.attributes[i]);delete this.attributes[i]}},this);this.set("metadata",h)},get_metadata:function(h){return this.attributes.metadata.get(h)},urlRoot:galaxy_paths.get("datasets_url")});var b=d.extend({defaults:_.extend({},d.prototype.defaults,{chunk_url:null,first_data_chunk:null,chunk_index:-1,at_eof:false}),initialize:function(h){d.prototype.initialize.call(this);this.attributes.chunk_index=(this.attributes.first_data_chunk?1:0)},get_next_chunk:function(){if(this.attributes.at_eof){return null}var h=this,i=$.Deferred();$.getJSON(this.attributes.chunk_url,{chunk:h.attributes.chunk_index++}).success(function(j){var k;if(j.ck_data!==""){k=j}else{h.attributes.at_eof=true;k=null}i.resolve(k)});return i}});var f=Backbone.Collection.extend({model:d});var e=Backbone.View.extend({initialize:function(h){},render:function(){this.$el.append($("<div/>").attr("id","loading_indicator"));var l=$("<table/>").attr({id:"content_table",cellpadding:0});this.$el.append(l);var h=this.model.get_metadata("column_names");if(h){l.append("<tr><th>"+h.join("</th><th>")+"</th></tr>")}var j=this.model.get("first_data_chunk");if(j){this._renderChunk(j)}var i=this,m=_.find(this.$el.parents(),function(n){return $(n).css("overflow")==="auto"}),k=false;if(!m){m=window}m=$(m);m.scroll(function(){if(!k&&(i.$el.height()-m.scrollTop()-m.height()<=0)){k=true;$.when(i.model.get_next_chunk()).then(function(n){if(n){i._renderChunk(n);k=false}})}});$("#loading_indicator").ajaxStart(function(){$(this).show()}).ajaxStop(function(){$(this).hide()})},_renderCell:function(j,h,k){var i=this.model.get_metadata("column_types");if(k!==undefined){return $("<td>").attr("colspan",k).addClass("stringalign").text(j)}else{if(i[h]==="str"||i==="list"){return $("<td>").addClass("stringalign").text(j)}else{return $("<td>").text(j)}}},_renderRow:function(h){var i=h.split("\t"),k=$("<tr>"),j=this.model.get_metadata("columns");if(i.length===j){_.each(i,function(m,l){k.append(this._renderCell(m,l))},this)}else{if(i.length>j){_.each(i.slice(0,j-1),function(m,l){k.append(this._renderCell(m,l))},this);k.append(this._renderCell(i.slice(j-1).join("\t"),j-1))}else{if(j>5&&i.length===j-1){_.each(i,function(m,l){k.append(this._renderCell(m,l))},this);k.append($("<td>"))}else{k.append(this._renderCell(h,0,j))}}}return k},_renderChunk:function(h){var i=this.$el.find("table");_.each(h.ck_data.split("\n"),function(j,k){i.append(this._renderRow(j))},this)}});var a=function(k,i,l,h){var j=new i({model:new k(l)});j.render();if(h){h.append(j.$el)}return j};var g=function(j,h){var i=$("<div/>").appendTo(h);return new e({el:i,model:new b(j)}).render()};return{Dataset:d,TabularDataset:b,DatasetCollection:f,TabularDatasetChunkedView:e,createTabularDatasetChunkedView:g}});
\ No newline at end of file
diff -r a6fe104c109feea61995567336a1aaf515acf0d5 -r d2e30720ba78cbee8b6a6a63daa91702778040bc static/scripts/packed/mvc/history/history-model.js
--- a/static/scripts/packed/mvc/history/history-model.js
+++ b/static/scripts/packed/mvc/history/history-model.js
@@ -1,1 +1,1 @@
-var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",diskSize:0,deleted:false,annotation:null,message:null},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b){if(_.isArray(b)){this.hdas.reset(b);this.checkForUpdates()}else{if(_.isString(b)&&(b.match(/error/i))){alert(_l("Error loading bootstrapped history")+":\n"+b)}}}},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.trigger("loaded:user",e[0]);b.trigger("loaded",d[0])}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.hdaIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();b.trigger("loaded:hdas",d);if(c){callback(b)}})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(f,d,e){if(!((f.readyState===0)&&(f.status===0))){alert(_l("Error getting history updates from the server.")+"\n"+e);c.log("stateUpdater error:",e,"responseText:",f.responseText)}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{var g=_l("ERROR updating hdas from api history contents")+": ";a.log(g,b,h,c,d,errorJSON);alert(g+b.join(","))}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
+var History=BaseModel.extend(LoggableMixin).extend({defaults:{id:"",name:"",state:"",diskSize:0,deleted:false,annotation:null,message:null},urlRoot:"api/histories/",url:function(){return"api/histories/"+this.get("id")},initialize:function(a,b){this.log(this+".initialize:",a,b);this.hdas=new HDACollection();if(b){if(_.isArray(b)){this.hdas.reset(b);this.checkForUpdates()}else{if(_.isString(b)&&(b.match(/error/i))){alert(_l("Error loading bootstrapped history")+":\n"+b)}}}},loadFromApi:function(a,c){var b=this;b.attributes.id=a;jQuery.when(jQuery.ajax("api/users/current"),b.fetch()).then(function(e,d){b.attributes.user=e[0];b.trigger("loaded:user",e[0]);b.trigger("loaded",d[0])}).then(function(){jQuery.ajax(b.url()+"/contents?"+jQuery.param({ids:b.hdaIdsFromStateIds().join(",")})).success(function(d){b.hdas.reset(d);b.checkForUpdates();b.trigger("loaded:hdas",d);if(c){callback(b)}})})},hdaIdsFromStateIds:function(){return _.reduce(_.values(this.get("state_ids")),function(b,a){return b.concat(a)})},checkForUpdates:function(a){if(this.hdas.running().length){this.stateUpdater()}else{this.trigger("ready")}return this},stateUpdater:function(){var c=this,a=this.get("state"),b=this.get("state_ids");jQuery.ajax("api/histories/"+this.get("id")).success(function(d){c.set(d);c.log("current history state:",c.get("state"),"(was)",a,"new size:",c.get("nice_size"));var e=[];_.each(_.keys(d.state_ids),function(g){var f=_.difference(d.state_ids[g],b[g]);e=e.concat(f)});if(e.length){c.fetchHdaUpdates(e)}if((c.get("state")===HistoryDatasetAssociation.STATES.RUNNING)||(c.get("state")===HistoryDatasetAssociation.STATES.QUEUED)){setTimeout(function(){c.stateUpdater()},History.UPDATE_DELAY)}else{c.trigger("ready")}}).error(function(f,d,e){if(!((f.readyState===0)&&(f.status===0))){alert(_l("Error getting history updates from the server.")+"\n"+e);c.log("stateUpdater error:",e,"responseText:",f.responseText)}})},fetchHdaUpdates:function(b){var a=this;jQuery.ajax({url:this.url()+"/contents?"+jQuery.param({ids:b.join(",")}),error:function(h,c,d){if((h.readyState===0)&&(h.status===0)){return}var f=JSON.parse(h.responseText);if(_.isArray(f)){var e=_.groupBy(f,function(i){if(_.has(i,"error")){return"errored"}return"ok"});a.log("fetched, errored datasets:",e.errored);a.updateHdas(f)}else{var g=_l("ERROR updating hdas from api history contents")+": ";a.log(g,b,h,c,d,f);alert(g+b.join(","))}},success:function(d,c,e){a.log(a+".fetchHdaUpdates, success:",c,e);a.updateHdas(d)}})},updateHdas:function(a){var c=this,b=[];c.log(c+".updateHdas:",a);_.each(a,function(e,f){var d=c.hdas.get(e.id);if(d){c.log("found existing model in list for id "+e.id+", updating...:");d.set(e)}else{c.log("NO existing model for id "+e.id+", creating...:");b.push(e)}});if(b.length){c.addHdas(b)}},addHdas:function(a){var b=this;_.each(a,function(c,d){var e=b.hdas.hidToCollectionIndex(c.hid);c.history_id=b.get("id");b.hdas.add(new HistoryDatasetAssociation(c),{at:e,silent:true})});b.hdas.trigger("add",a)},toString:function(){var a=(this.get("name"))?(","+this.get("name")):("");return"History("+this.get("id")+a+")"}});History.UPDATE_DELAY=4000;var HistoryCollection=Backbone.Collection.extend(LoggableMixin).extend({model:History,urlRoot:"api/histories"});
\ No newline at end of file
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.
1
0
3 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/1f1072317efd/
changeset: 1f1072317efd
branch: stable
user: natefoo
date: 2013-02-12 14:46:28
summary: Allow the CLI runner to handle command stdout >65K.
affected #: 2 files
diff -r 961ae35ba61230ac1f5991b7629e5da0559cd0b5 -r 1f1072317efd5cf82cb791d75ba968c4bacffaf4 lib/galaxy/jobs/runners/cli.py
--- a/lib/galaxy/jobs/runners/cli.py
+++ b/lib/galaxy/jobs/runners/cli.py
@@ -316,6 +316,7 @@
if which_try == self.app.config.retry_job_output_collection:
stdout = ''
stderr = 'Job output not returned from cluster'
+ exit_code = 0
log.debug( stderr )
else:
time.sleep(1)
diff -r 961ae35ba61230ac1f5991b7629e5da0559cd0b5 -r 1f1072317efd5cf82cb791d75ba968c4bacffaf4 lib/galaxy/jobs/runners/cli_shell/rsh.py
--- a/lib/galaxy/jobs/runners/cli_shell/rsh.py
+++ b/lib/galaxy/jobs/runners/cli_shell/rsh.py
@@ -4,6 +4,7 @@
import time
import logging
+import tempfile
import subprocess
from galaxy.util.bunch import Bunch
@@ -28,7 +29,9 @@
fullcmd = '%s %s %s' % (self.rsh, self.hostname, cmd)
else:
fullcmd = '%s -l %s %s %s' % (self.rsh, self.username, self.hostname, cmd)
- p = subprocess.Popen(fullcmd, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ # Read stdout to a tempfile in case it's large (>65K)
+ outf = tempfile.TemporaryFile()
+ p = subprocess.Popen(fullcmd, shell=True, stdin=None, stdout=outf, stderr=subprocess.PIPE)
# poll until timeout
for i in range(timeout/3):
r = p.poll()
@@ -44,7 +47,8 @@
except:
log.warning('Killing pid %s (cmd: "%s") with signal %s failed' % (p.pid, fullcmd, sig))
return Bunch(stdout='', stderr='Execution timed out', returncode=-1)
- return Bunch(stdout=p.stdout.read(), stderr=p.stderr.read(), returncode=p.returncode)
+ outf.seek(0)
+ return Bunch(stdout=outf.read(), stderr=p.stderr.read(), returncode=p.returncode)
class SecureShell(RemoteShell):
https://bitbucket.org/galaxy/galaxy-central/commits/056ff69b05c4/
changeset: 056ff69b05c4
user: natefoo
date: 2013-02-12 14:46:28
summary: Allow the CLI runner to handle command stdout >65K.
affected #: 2 files
diff -r e5dcefc328bb4fadcd0097787d167c7f43db5627 -r 056ff69b05c435f26650c5e7f65cffd764a40a08 lib/galaxy/jobs/runners/cli.py
--- a/lib/galaxy/jobs/runners/cli.py
+++ b/lib/galaxy/jobs/runners/cli.py
@@ -316,6 +316,7 @@
if which_try == self.app.config.retry_job_output_collection:
stdout = ''
stderr = 'Job output not returned from cluster'
+ exit_code = 0
log.debug( stderr )
else:
time.sleep(1)
diff -r e5dcefc328bb4fadcd0097787d167c7f43db5627 -r 056ff69b05c435f26650c5e7f65cffd764a40a08 lib/galaxy/jobs/runners/cli_shell/rsh.py
--- a/lib/galaxy/jobs/runners/cli_shell/rsh.py
+++ b/lib/galaxy/jobs/runners/cli_shell/rsh.py
@@ -4,6 +4,7 @@
import time
import logging
+import tempfile
import subprocess
from galaxy.util.bunch import Bunch
@@ -28,7 +29,9 @@
fullcmd = '%s %s %s' % (self.rsh, self.hostname, cmd)
else:
fullcmd = '%s -l %s %s %s' % (self.rsh, self.username, self.hostname, cmd)
- p = subprocess.Popen(fullcmd, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ # Read stdout to a tempfile in case it's large (>65K)
+ outf = tempfile.TemporaryFile()
+ p = subprocess.Popen(fullcmd, shell=True, stdin=None, stdout=outf, stderr=subprocess.PIPE)
# poll until timeout
for i in range(timeout/3):
r = p.poll()
@@ -44,7 +47,8 @@
except:
log.warning('Killing pid %s (cmd: "%s") with signal %s failed' % (p.pid, fullcmd, sig))
return Bunch(stdout='', stderr='Execution timed out', returncode=-1)
- return Bunch(stdout=p.stdout.read(), stderr=p.stderr.read(), returncode=p.returncode)
+ outf.seek(0)
+ return Bunch(stdout=outf.read(), stderr=p.stderr.read(), returncode=p.returncode)
class SecureShell(RemoteShell):
https://bitbucket.org/galaxy/galaxy-central/commits/a6fe104c109f/
changeset: a6fe104c109f
user: natefoo
date: 2013-02-12 15:04:17
summary: Merged heads.
affected #: 2 files
diff -r 056ff69b05c435f26650c5e7f65cffd764a40a08 -r a6fe104c109feea61995567336a1aaf515acf0d5 tools/ngs_rna/tophat2_wrapper.py
--- a/tools/ngs_rna/tophat2_wrapper.py
+++ b/tools/ngs_rna/tophat2_wrapper.py
@@ -22,7 +22,7 @@
parser.add_option( '', '--mate-std-dev', dest='mate_std_dev', help='Standard deviation of distribution on inner distances between male pairs.' )
parser.add_option( '', '--read-mismatches', dest='read_mismatches' )
parser.add_option( '', '--bowtie-n', action="store_true", dest='bowtie_n' )
- parser.add_option( '', '--report-discordant-pair-alignments', action="store_true", dest='report_discordant_pairs' )
+ parser.add_option( '', '--no-discordant', action="store_true", dest='report_concordant_pairs_only' )
parser.add_option( '-a', '--min-anchor-length', dest='min_anchor_length',
help='The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction.' )
parser.add_option( '-m', '--splice-mismatches', dest='splice_mismatches', help='The maximum number of mismatches that can appear in the anchor region of a spliced alignment.' )
@@ -141,8 +141,8 @@
opts = '-p %s %s' % ( options.num_threads, space )
if options.single_paired == 'paired':
opts += ' -r %s' % options.mate_inner_dist
- if options.report_discordant_pairs:
- opts += ' --report-discordant-pair-alignments'
+ if options.report_concordant_pairs_only:
+ opts += ' --no-discordant'
# Read group options.
if options.rgid:
if not options.rglb or not options.rgpl or not options.rgsm:
diff -r 056ff69b05c435f26650c5e7f65cffd764a40a08 -r a6fe104c109feea61995567336a1aaf515acf0d5 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -37,8 +37,8 @@
-r $singlePaired.mate_inner_distance
--mate-std-dev=$singlePaired.mate_std_dev
- #if str($singlePaired.report_discordant_pairs) == "Yes":
- --report-discordant-pair-alignments
+ #if str($singlePaired.report_discordant_pairs) == "No":
+ --no-discordant
#end if
#end if
@@ -138,8 +138,8 @@
<param name="mate_std_dev" type="integer" value="20" label="Std. Dev for Distance between Mate Pairs" help="The standard deviation for the distribution on inner distances between mate pairs."/><!-- Discordant pairs. --><param name="report_discordant_pairs" type="select" label="Report discordant pair alignments?">
- <option selected="true" value="No">No</option>
- <option value="Yes">Yes</option>
+ <option value="No">No</option>
+ <option selected="True" value="Yes">Yes</option></param></when></conditional>
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.
1
0
commit/galaxy-central: joachimjacob: Fixed --no-discordant parameter in tophat2_wrapper.xml and .py.
by Bitbucket 12 Feb '13
by Bitbucket 12 Feb '13
12 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/b1d3a6a907d1/
changeset: b1d3a6a907d1
user: joachimjacob
date: 2013-02-12 09:55:44
summary: Fixed --no-discordant parameter in tophat2_wrapper.xml and .py.
affected #: 2 files
diff -r e5dcefc328bb4fadcd0097787d167c7f43db5627 -r b1d3a6a907d1ee4847fbcea61a162067b3be0f49 tools/ngs_rna/tophat2_wrapper.py
--- a/tools/ngs_rna/tophat2_wrapper.py
+++ b/tools/ngs_rna/tophat2_wrapper.py
@@ -22,7 +22,7 @@
parser.add_option( '', '--mate-std-dev', dest='mate_std_dev', help='Standard deviation of distribution on inner distances between male pairs.' )
parser.add_option( '', '--read-mismatches', dest='read_mismatches' )
parser.add_option( '', '--bowtie-n', action="store_true", dest='bowtie_n' )
- parser.add_option( '', '--report-discordant-pair-alignments', action="store_true", dest='report_discordant_pairs' )
+ parser.add_option( '', '--no-discordant', action="store_true", dest='report_concordant_pairs_only' )
parser.add_option( '-a', '--min-anchor-length', dest='min_anchor_length',
help='The "anchor length". TopHat will report junctions spanned by reads with at least this many bases on each side of the junction.' )
parser.add_option( '-m', '--splice-mismatches', dest='splice_mismatches', help='The maximum number of mismatches that can appear in the anchor region of a spliced alignment.' )
@@ -141,8 +141,8 @@
opts = '-p %s %s' % ( options.num_threads, space )
if options.single_paired == 'paired':
opts += ' -r %s' % options.mate_inner_dist
- if options.report_discordant_pairs:
- opts += ' --report-discordant-pair-alignments'
+ if options.report_concordant_pairs_only:
+ opts += ' --no-discordant'
# Read group options.
if options.rgid:
if not options.rglb or not options.rgpl or not options.rgsm:
diff -r e5dcefc328bb4fadcd0097787d167c7f43db5627 -r b1d3a6a907d1ee4847fbcea61a162067b3be0f49 tools/ngs_rna/tophat2_wrapper.xml
--- a/tools/ngs_rna/tophat2_wrapper.xml
+++ b/tools/ngs_rna/tophat2_wrapper.xml
@@ -37,8 +37,8 @@
-r $singlePaired.mate_inner_distance
--mate-std-dev=$singlePaired.mate_std_dev
- #if str($singlePaired.report_discordant_pairs) == "Yes":
- --report-discordant-pair-alignments
+ #if str($singlePaired.report_discordant_pairs) == "No":
+ --no-discordant
#end if
#end if
@@ -138,8 +138,8 @@
<param name="mate_std_dev" type="integer" value="20" label="Std. Dev for Distance between Mate Pairs" help="The standard deviation for the distribution on inner distances between mate pairs."/><!-- Discordant pairs. --><param name="report_discordant_pairs" type="select" label="Report discordant pair alignments?">
- <option selected="true" value="No">No</option>
- <option value="Yes">Yes</option>
+ <option value="No">No</option>
+ <option selected="True" value="Yes">Yes</option></param></when></conditional>
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.
1
0
commit/galaxy-central: inithello: Uncomment the previously added sentry_dsn.
by Bitbucket 11 Feb '13
by Bitbucket 11 Feb '13
11 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e5dcefc328bb/
changeset: e5dcefc328bb
user: inithello
date: 2013-02-12 05:17:13
summary: Uncomment the previously added sentry_dsn.
affected #: 1 file
diff -r 6be149267ed6016351819a0605213fb3d34b25ef -r e5dcefc328bb4fadcd0097787d167c7f43db5627 lib/galaxy/webapps/reports/config.py
--- a/lib/galaxy/webapps/reports/config.py
+++ b/lib/galaxy/webapps/reports/config.py
@@ -41,7 +41,7 @@
self.log_events = False
self.cookie_path = kwargs.get( "cookie_path", "/" )
# Error logging with sentry
- # self.sentry_dsn = kwargs.get( 'sentry_dsn', None )
+ self.sentry_dsn = kwargs.get( 'sentry_dsn', None )
#Parse global_conf
global_conf = kwargs.get( 'global_conf', None )
global_conf_parser = ConfigParser.ConfigParser()
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.
1
0
commit/galaxy-central: inithello: Add sentry_dsn to reports webapp configuration.
by Bitbucket 11 Feb '13
by Bitbucket 11 Feb '13
11 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6be149267ed6/
changeset: 6be149267ed6
user: inithello
date: 2013-02-12 05:15:34
summary: Add sentry_dsn to reports webapp configuration.
affected #: 1 file
diff -r 42e0e5d9b7a479d884f9556a59aadea7886c131e -r 6be149267ed6016351819a0605213fb3d34b25ef lib/galaxy/webapps/reports/config.py
--- a/lib/galaxy/webapps/reports/config.py
+++ b/lib/galaxy/webapps/reports/config.py
@@ -40,6 +40,8 @@
self.screencasts_url = kwargs.get( 'screencasts_url', None )
self.log_events = False
self.cookie_path = kwargs.get( "cookie_path", "/" )
+ # Error logging with sentry
+ # self.sentry_dsn = kwargs.get( 'sentry_dsn', None )
#Parse global_conf
global_conf = kwargs.get( 'global_conf', None )
global_conf_parser = ConfigParser.ConfigParser()
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.
1
0
commit/galaxy-central: natefoo: Return a dict instead of a list if a [galaxy:tool_*] section is missing from the config.
by Bitbucket 11 Feb '13
by Bitbucket 11 Feb '13
11 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/42e0e5d9b7a4/
changeset: 42e0e5d9b7a4
user: natefoo
date: 2013-02-11 22:50:16
summary: Return a dict instead of a list if a [galaxy:tool_*] section is missing from the config.
affected #: 1 file
diff -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 -r 42e0e5d9b7a479d884f9556a59aadea7886c131e lib/galaxy/config.py
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -304,7 +304,7 @@
return rval
except ConfigParser.NoSectionError:
- return []
+ return {}
def get( self, key, default ):
return self.config_dict.get( key, default )
def get_bool( self, key, default ):
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.
1
0
commit/galaxy-central: jgoecks: Use tabular chunked data diplay for shared/published datasets.
by Bitbucket 11 Feb '13
by Bitbucket 11 Feb '13
11 Feb '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/41f4c9c90959/
changeset: 41f4c9c90959
user: jgoecks
date: 2013-02-11 21:56:50
summary: Use tabular chunked data diplay for shared/published datasets.
affected #: 5 files
diff -r 422f368eb0978b7d8ed5a320f1700603aea22ca1 -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 lib/galaxy/datatypes/data.py
--- a/lib/galaxy/datatypes/data.py
+++ b/lib/galaxy/datatypes/data.py
@@ -69,6 +69,9 @@
<class 'galaxy.datatypes.metadata.MetadataParameter'>
"""
+ # Data is not chunkable by default.
+ CHUNKABLE = False
+
#: dictionary of metadata fields for this datatype::
metadata_spec = None
diff -r 422f368eb0978b7d8ed5a320f1700603aea22ca1 -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 lib/galaxy/datatypes/tabular.py
--- a/lib/galaxy/datatypes/tabular.py
+++ b/lib/galaxy/datatypes/tabular.py
@@ -20,6 +20,9 @@
class Tabular( data.Text ):
"""Tab delimited data"""
+
+ # All tabular data is chunkable.
+ CHUNKABLE = True
CHUNK_SIZE = 50000
"""Add metadata elements"""
diff -r 422f368eb0978b7d8ed5a320f1700603aea22ca1 -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -642,6 +642,11 @@
truncated, dataset_data = self.get_data( dataset, preview )
dataset.annotation = self.get_item_annotation_str( trans.sa_session, dataset.history.user, dataset )
+ # If dataset is chunkable, get first chunk.
+ first_chunk = None
+ if dataset.datatype.CHUNKABLE:
+ first_chunk = dataset.datatype.get_chunk(trans, dataset, 0)
+
# If data is binary or an image, stream without template; otherwise, use display template.
# TODO: figure out a way to display images in display template.
if isinstance(dataset.datatype, datatypes.binary.Binary) or isinstance(dataset.datatype, datatypes.images.Image) or isinstance(dataset.datatype, datatypes.images.Html):
@@ -658,8 +663,10 @@
user_item_rating = 0
ave_item_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, dataset )
- return trans.fill_template_mako( "/dataset/display.mako", item=dataset, item_data=dataset_data, truncated=truncated,
- user_item_rating = user_item_rating, ave_item_rating=ave_item_rating, num_ratings=num_ratings )
+ return trans.fill_template_mako( "/dataset/display.mako", item=dataset, item_data=dataset_data,
+ truncated=truncated, user_item_rating = user_item_rating,
+ ave_item_rating=ave_item_rating, num_ratings=num_ratings,
+ first_chunk=first_chunk )
else:
raise web.httpexceptions.HTTPNotFound()
diff -r 422f368eb0978b7d8ed5a320f1700603aea22ca1 -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 static/scripts/mvc/data.js
--- a/static/scripts/mvc/data.js
+++ b/static/scripts/mvc/data.js
@@ -102,7 +102,10 @@
});
/**
- * Provides table-based, dynamic view of a tabular dataset.
+ * Provides table-based, dynamic view of a tabular dataset.
+ * NOTE: view's el must be in DOM already and provided when
+ * createing the view so that scrolling event can be attached
+ * to the correct container.
*/
var TabularDatasetChunkedView = Backbone.View.extend({
@@ -129,13 +132,31 @@
this._renderChunk(first_chunk);
}
- // Show new chunks during scrolling.
- var self = this;
- $(window).scroll(function() {
- if ($(window).scrollTop() === $(document).height() - $(window).height()) {
+ // -- Show new chunks during scrolling. --
+
+ var self = this,
+ // Element that does the scrolling.
+ scroll_elt = _.find(this.$el.parents(), function(p) {
+ return $(p).css('overflow') === 'auto';
+ }),
+ // Flag to ensure that only one chunk is loaded at a time.
+ loading_chunk = false;
+
+ // If no scrolling element found, use window.
+ if (!scroll_elt) { scroll_elt = window; }
+
+ // Wrap scrolling element for easy access.
+ scroll_elt = $(scroll_elt);
+
+ // Set up chunk loading when scrolling using the scrolling element.
+ scroll_elt.scroll(function() {
+ // If not already loading a chunk and have scrolled to the bottom of this element, get next chunk.
+ if ( !loading_chunk && (self.$el.height() - scroll_elt.scrollTop() - scroll_elt.height() <= 0) ) {
+ loading_chunk = true;
$.when(self.model.get_next_chunk()).then(function(result) {
if (result) {
self._renderChunk(result);
+ loading_chunk = false;
}
});
}
@@ -218,7 +239,7 @@
if (parent_elt) {
parent_elt.append(a_view.$el);
}
-
+
return a_view;
};
@@ -227,7 +248,14 @@
* and appends to parent_elt.
*/
var createTabularDatasetChunkedView = function(dataset_config, parent_elt) {
- return createModelAndView(TabularDataset, TabularDatasetChunkedView, dataset_config, parent_elt);
+ // Create view element and add to parent.
+ var view_div = $('<div/>').appendTo(parent_elt);
+
+ // Create view with model, render, and return.
+ return new TabularDatasetChunkedView({
+ el: view_div,
+ model: new TabularDataset(dataset_config)
+ }).render();
};
return {
diff -r 422f368eb0978b7d8ed5a320f1700603aea22ca1 -r 41f4c9c9095919b080dc0e32a5d540865fdf0fa7 templates/webapps/galaxy/dataset/display.mako
--- a/templates/webapps/galaxy/dataset/display.mako
+++ b/templates/webapps/galaxy/dataset/display.mako
@@ -5,6 +5,35 @@
<%def name="javascripts()">
${parent.javascripts()}
+ ## If data is chunkable, use JavaScript for display.
+ %if item.datatype.CHUNKABLE:
+
+ <script type="text/javascript">
+ require.config({
+ baseUrl: "${h.url_for('/static/scripts')}",
+ shim: {
+ "libs/backbone/backbone": { exports: "Backbone" },
+ "libs/backbone/backbone-relational": ["libs/backbone/backbone"]
+ }
+ });
+
+ require(['mvc/data'], function(data) {
+ data.createTabularDatasetChunkedView(
+ // Dataset config. TODO: encode id.
+ _.extend( ${h.to_json_string( item.get_api_value() )},
+ {
+ chunk_url: "${h.url_for( controller='/dataset', action='display',
+ dataset_id=trans.security.encode_id( item.id ))}",
+ first_data_chunk: ${first_chunk}
+ }
+ ),
+ // Append view to body.
+ $('.page-body')
+ );
+ });
+ </script>
+
+ %endif
</%def><%def name="init()">
@@ -31,14 +60,17 @@
</%def><%def name="render_item( data, data_to_render )">
- %if truncated:
- <div class="warningmessagelarge">
- This dataset is large and only the first megabyte is shown below. |
- <a href="${h.url_for( controller='dataset', action='display_by_username_and_slug', username=data.history.user.username, slug=trans.security.encode_id( data.id ), preview=False )}">Show all</a>
- </div>
+ ## Chunkable data is rendered in JavaScript above; render unchunkable data below.
+ %if not data.datatype.CHUNKABLE:
+ %if truncated:
+ <div class="warningmessagelarge">
+ This dataset is large and only the first megabyte is shown below. |
+ <a href="${h.url_for( controller='dataset', action='display_by_username_and_slug', username=data.history.user.username, slug=trans.security.encode_id( data.id ), preview=False )}">Show all</a>
+ </div>
+ %endif
+ ## TODO: why is the default font size so small?
+ <pre style="font-size: 135%">${ data_to_render | h }</pre>
%endif
- ## TODO: why is the default font size so small?
- <pre style="font-size: 135%">${ data_to_render | h }</pre></%def>
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.
1
0