/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
var CKTESTER =
{
fort :
{
registeredCells : [],
pendingCells : [], // The cells that are pending to run.
profile : {}, // Override in 'profile.js' file.
variables : {}, // Override in 'variables.js' file.
criterias : [],
/*
currentCell : // The solved current running cell.
{
path : '../editor/tt/unit/htmldataprocessor',
tags : [ 'editor', 'tt', 'unit', 'htmldataprocessor' ],
environments : [ 'runners/yuitest/yuitest.js',
'${EDITORPATH}/ckeditor.js',
'extensions/yuitest/extension.js'
]
},
*/
currentTime : 0,
totalTime : 0,
totalFailed : 0,
totalPassed : 0,
testFrame : null,
defaultProfilePath : '../profile.js',
defaultVariables : '../variables.js',
defaultRunnerRoot : '/cktester',
init : function()
{
this.testFrame = $( 'testFrame' );
this.processProfile();
this.initUI();
},
bootstrap : function()
{
function getUrlParam( paramName )
{
var pattern = '=(.*?)(?:&|%26|$)',
match;
return ( match = query.match( new RegExp( paramName + pattern ) ) ) && match[ 1 ]
}
var url = window.location.href;
var baseUrl = url.substr( 0, url.search( /[^\/\\]*\.\w+(:?$|\?)/ ) ),
query = window.location.search, match,
// Receive 'profile','variables' and 'criteria' from URL parts.
profile = getUrlParam( 'profile' ) || this.defaultProfilePath,
variables = getUrlParam( 'variables' ) || this.defaultVariables,
criterias = ( getUrlParam( 'cells' ) || '' ).split( ',' ),
// Single cell if cell absolute path is specified as criteria.
singleCell = criterias[ 0 ] && criterias[ 0 ].indexOf( '://' ) != -1,
// Force cell path as relative to fort root.
cellPath = singleCell && this.getRelativePath( baseUrl, criterias[ 0 ].replace( /\.\w+$/, '' ) ),
// Receive tags defined inside the cell itself.
cellTags = singleCell && ( getUrlParam( 'tags' ) || '' ).split( ',' );
// Include variables and profile definitions.
document.open();
document.write( '' );
document.close();
window.onload = ( function()
{
this.profile = this.getProfile();
!singleCell && ( this.criterias = criterias );
// Ignore the profile cell definition, if only with the specified single cell .
if ( singleCell )
{
this.profile.cells = [ [ cellPath, cellTags ] ];
this.profile.singleCell = true;
}
this.init();
// Auto start only if there's criteria found OR it's single cell.
if ( this.criterias.length || singleCell )
this.runAll();
}.bind( this ) );
},
initUI : function()
{
// Build criteria auto-complete.
var tagsDataSource = new YAHOO.util.LocalDataSource( this.registeredTags );
var startButton = $( 'start-test-btn' ),
resetButton = $( 'reload-fort-btn' ),
tagsInput = $( 'tagsInput' ),
tagsAutoComp = new YAHOO.widget.AutoComplete( tagsInput,"tagsContainer", tagsDataSource ),
start = function()
{
this.criterias = tagsInput.value.split( ' ' ).without( '' );
this.runAll();
}.bind( this ),
reset = function()
{
document.location.search = '';
};
tagsAutoComp.delimChar = ' ';
tagsAutoComp.typeAhead = true;
tagsAutoComp.useShadow = true;
startButton.observe( 'click', start );
resetButton.observe( 'click', reset );
},
processProfile : function ()
{
var profile = this.profile,
cells = profile.cells,
resolvers = profile.cellResolvers,
tags = [];
// Merge resolvers in profile.
this.cellResolvers = this.cellResolvers.concat( resolvers );
// Sort the resolvers by function names alphabetically,
// which indicate priority, anonymous ones get 'g'.
this.cellResolvers = this.cellResolvers.sortBy( function( func )
{
var match;
return func.name
|| ( match = func.toString().match( /function ([^\s]+)\(/ ) ) && match[ 1 ]
|| 'l';
} );
for ( var i = 0 ; i < cells.length ; i++ )
{
var cell = cells[ i ],
cell =
{
path : profile.singleCell ?
cell[ 0 ]
: this.getRelativePath( this.defaultRunnerRoot, cell [ 0 ] ),
tags : cell[ 1 ] || [],
environment : cell[ 2 ] || []
};
for ( var j = 0 ; j < this.cellResolvers.length ; j++ )
this.cellResolvers[ j ].call( this, cell );
tags = tags.concat( cell.tags );
this.registeredCells.push( cell );
}
// Cache the unique tags,
// TODO: Count tag frequency.
this.registeredTags = tags.uniq();
},
runAll : function()
{
this.reset();
if ( !this.registeredCells.length )
{
throw 'None test cell found!';
return;
}
var criterias = this.criterias;
this.pendingCells = criterias.length ? this.registeredCells.findAll( function( cell ){
return ( criterias.indexOf( cell.path ) != -1
|| cell.tags.detect( function( tag )
{
return criterias.indexOf( tag ) != -1;
} ) );
} ) : this.registeredCells.clone();
this.pendingCells.each( this.resolvePath, this );
this.runCell();
},
runCell : function()
{
var cell = ( this.currentCell = this.pendingCells.shift() );
if( cell )
{
var inNewWindow = cell.tags.indexOf( 'manual' ) != -1;
// Force new window + managed mode for cells with manual tag.
inNewWindow ?
this.runSingleCell( this.currentCell, 'managed' )
: this.testFrame.setAttribute( 'src', this.currentCell.path + '.html' );
}
// No more pending cells, close this test now.
else
this.allComplete();
},
runSingleCell : function( cell, mode )
{
this.currentCell = cell;
cell.mode = mode || 'standalone';
// Open the test frame in a new popup window.
setTimeout( function()
{
window.open( cell.path + '.html' );
}, 0 );
},
setupCellLink : function( cellBlock, cell )
{
$( cellBlock ).childElements().grep( new Selector( 'a' ) )[ 0 ]
.observe( 'click', this.runSingleCell.curry( cell ).bind( this ) );
},
cellStart : function( cell )
{
var div = $( 'testLogger' ).appendChild( document.createElement( 'div' ) );
div.className = 'testEntry';
div.innerHTML = 'Testing "' + cell.name + '"...';
this.setupCellLink( div, cell );
this.currentTime = new Date();
if ( !this.totalTime )
this.totalTime = this.currentTime;
},
cellComplete : function( cell )
{
var finishTime = new Date(),
results = cell.data.results;
var failed = results.failed;
var passed = results.passed;
var html = 'Unknown';
if ( failed > 0 )
html = 'FAIL';
else
html = 'PASS';
html += ' Test "' + cell.name + '" (' + failed + ' failed / ' + passed + ' passed) - ' + ( finishTime - this.currentTime ) + 'ms';
var div = $('testLogger').lastChild;
div.innerHTML = html;
$( div ).childElements().grep( new Selector( 'a' ) )[ 0 ]
.observe( 'click', this.runSingleCell.curry( cell ).bind( this ) );
this.totalFailed += failed;
this.totalPassed += passed;
$('testFailed').innerHTML = this.totalFailed;
$('testPassed').innerHTML = this.totalPassed;
$('totalTime').innerHTML = finishTime - this.totalTime;
document.title = this.totalFailed + ' failed / ' + this.totalPassed + ' passed - CKEditor Core Tests Runner';
var self = this;
setTimeout( function(){
self.runCell();
}, 0 );
},
allComplete : function()
{
this.testFrame. hide();
},
reset : function()
{
this.pendingCells = [];
this.currentCell = null;
this.currentTime = 0 ;
this.totalTime = 0 ;
this.totalPassed = 0 ;
this.totalFailed = 0;
$( 'testLogger' ).innerHTML = '';
},
replaceVars : function( str )
{
for( var i in this.variables )
str = str.replace( new RegExp( "\\$({|%7B)" + i + "(}|%7D)", 'g' ), this.variables[ i ] || '' ) ;
return str ;
},
getAbsolutePath : function( win, path )
{
// If this is not a full or absolute path.
if ( path.indexOf('://') == -1 && path.indexOf( '/' ) !== 0 )
{
// Webkit bug: Avoid requesting with original file name (MIME type)
// which will stop browser from interpreting resources from same URL.
var suffixIndex = path.lastIndexOf( '.' ),
suffix = suffixIndex == -1 ? '' : path.substring( suffixIndex, path.length );
suffix && ( path = path.substring( 0, suffixIndex ) );
win = ( win || window );
var temp = win.document.createElement( 'img' );
temp.src = path;
return temp.src + suffix;
}
else
return path;
},
// Calculate file with 'pathB's relative path to file with 'pathA'.
getRelativePath : function ( pathA, pathB )
{
var deliminator = /[\/\\]+/,
normalizePath = function ( path )
{
// Always start with backslash and end with no slashes.
return path.replace( /^[^\/\\]/, '/$&' ).replace( /[\/\\]+$/,'' );
},
pathA = normalizePath( pathA ).split( deliminator ),
pathB = normalizePath( pathB ).split( deliminator ),
length = pathA.length,
result = [];
for( var i = 0 ; i < length; i++ )
{
if( pathA[ i ] != pathB [ i ] )
break;
}
if( length - i > 0 )
{
result = result.concat( $R( 1, length - i ).collect( function( n ){ return '..' ; } ) );
}
result = result.concat( pathB.slice( i ) );
return result.join( '/' );
},
// Figure out the real paths of cell's environments.
resolvePath : function ( cell )
{
for ( var i = 0 ; i < cell.environment.length ; i++ )
{
cell.environment[ i ] = this.getAbsolutePath( null, this.replaceVars( cell.environment[ i ] ) );
}
},
// Defined default tag resolvers.
cellResolvers : [
// Build tags out of path.
function a( cell )
{
var path = cell.path, tags = cell.tags;
path.replace( /([^.\/]+)[\/\\]+/g, function( match, tag )
{
if ( tags && tags.indexOf( tag ) == -1 )
tags.push( tag );
} );
},
// Default environment injections.
function b( cell )
{
var tags = cell.tags, env = [];
if( tags.indexOf( 'unit' ) != -1 )
env = env.concat( [ 'runners/yuitest/yuitest.js',
'runners/yuitest/yuitest.css',
'runners/yuitest/extension.js',
'runners/yuitest/test.js' ] );
// Manual testing environments.
if( tags.indexOf( 'manual' ) != -1 )
env = env.concat( [ 'runners/manual/yui-resize.css',
'runners/manual/yui-resize.js',
'runners/manual/manual.js' ] );
if ( tags.indexOf( 'selenium' ) != -1 )
env = env.concat( [ 'runners/selenium/selenium.js',
'runners/selenium/extensions.js' ] );
cell.environment = env.concat( cell.environment );
}
]
}
};
CKTESTER.fort.bootstrap();