blob: 41c15c73560911fedde82d12fe8a881d0f4524cd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2015 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.rap.jstestrunner.jasmine;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.rap.jstestrunner.jasmine.JasmineReporter.Spec;
import org.eclipse.rap.jstestrunner.jasmine.JasmineReporter.Suite;
import org.mozilla.javascript.*;
class JasmineRunner {
private static final String CHARSET = "UTF-8";
private final ScriptableObject scope;
private final ScriptableObject jasmineEnv;
private final Map<String, Object> resources = new HashMap<>();
private JasmineReporter publicReporter;
public JasmineRunner() {
scope = Context.enter().initStandardObjects();
initializeScope();
parseScript( getClass().getClassLoader(), "org/eclipse/rap/jstestrunner/jasmine/jasmine.js" );
jasmineEnv = getJasmineEnv();
createReporter();
Context.exit();
}
public void setReporter( JasmineReporter publicReporter ) {
this.publicReporter = publicReporter;
}
public ScriptableObject getScope() {
return scope;
}
public void parseScript( ClassLoader loader, String path ) {
String script = readContent( loader, path );
Context.enter();
Context context = Context.getCurrentContext();
try {
context.evaluateString( scope, script, path, 1, null );
} catch( EcmaError ex ) {
String message = "Could not execute "
+ path
+ "\n"
+ ex.getErrorMessage()
+ "\n"
+ ex.getScriptStackTrace();
throw new IllegalStateException( message );
}
Context.exit();
}
public void addResource( String name, ClassLoader loader, String path ) {
resources.put( name, readContent( loader, path ) );
}
public void execute() {
Context.enter();
ScriptableObject.putProperty( jasmineEnv, "updateInterval", Integer.valueOf( 0 ) );
ScriptableObject.callMethod( jasmineEnv, "execute", null );
Context.exit();
}
////////////
// Internals
private void initializeScope() {
scope.put( "TestUtil", scope, new TestUtil() );
scope.put( "window", scope, scope );
createStubs( "setTimeout", "clearTimeout", "setInterval", "clearInterval" );
}
private void createReporter() {
ScriptableObject.callMethod( jasmineEnv, "addReporter", new Object[]{ new InternalReporter() } );
}
private ScriptableObject getJasmineEnv() {
ScriptableObject jasmine = ( ScriptableObject )scope.get( "jasmine" );
return ( ScriptableObject )ScriptableObject.callMethod( jasmine, "getEnv", null );
}
private void createStubs( final String... names ) {
for( String name : names ) {
createStub( name );
}
}
private void createStub( final String name ) {
scope.put( name, scope, new BaseFunction() {
@Override
public Object call( Context cx, Scriptable scope, Scriptable thisObj, Object[] args ) {
throw new UnsupportedOperationException( name + " is not implemented" );
}
} );
}
private static String readContent( ClassLoader loader, String resource ) {
try {
return readTextContentChecked( loader, resource );
} catch( IOException ioe ) {
throw new IllegalArgumentException( "Failed to read resource: " + resource, ioe );
}
}
private static String readTextContentChecked( ClassLoader loader, String resource ) throws IOException {
InputStream stream = loader.getResourceAsStream( resource );
if( stream == null ) {
throw new IllegalArgumentException( "Resource not found: " + resource );
}
try {
BufferedReader reader = new BufferedReader( new InputStreamReader( stream, CHARSET ) );
return readLines( reader );
} finally {
stream.close();
}
}
private static String readLines( BufferedReader reader ) throws IOException {
StringBuilder builder = new StringBuilder();
String line = reader.readLine();
while( line != null ) {
builder.append( line );
builder.append( '\n' );
line = reader.readLine();
}
return builder.toString();
}
/*
* Made available to JS code by Rhino, unused parameters are therefore required.
*/
@SuppressWarnings( "unused" )
public class InternalReporter {
public void reportRunnerStarting( ScriptableObject runner ) {
publicReporter.reportRunnerStarting();
}
public void reportRunnerResults( ScriptableObject runner ) {
publicReporter.reportRunnerResults();
}
public void reportSpecStarting( ScriptableObject specObj ) {
publicReporter.reportSpecStarting( createSpec( specObj ) );
}
public void reportSpecResults( ScriptableObject specObj ) {
publicReporter.reportSpecResults( createSpec( specObj ) );
}
public void reportSuiteResults( ScriptableObject suiteObj ) {
publicReporter.reportSuiteResults( createSuite( suiteObj ) );
}
public void log( String message ) {
publicReporter.log( message );
}
private Spec createSpec( ScriptableObject specObj ) {
Suite suite = createSuite( ( ScriptableObject )specObj.get( "suite" ) );
String description = ( String )specObj.get( "description" );
ScriptableObject results
= ( ScriptableObject )ScriptableObject.callMethod( specObj, "results", null );
boolean passed
= ( ( Boolean )ScriptableObject.callMethod( results, "passed", null ) ).booleanValue();
String error = getMessageForSpec( results );
return new SpecImpl( suite, description, passed, error );
}
private String getMessageForSpec( ScriptableObject resultsObj ) {
String result = "";
ScriptableObject items
= ( ( ScriptableObject )ScriptableObject.callMethod( resultsObj, "getItems", null ) );
for( Object id : items.getIds() ) {
ScriptableObject item = ( ScriptableObject )items.get( id );
boolean itemPassed
= ( ( Boolean )ScriptableObject.callMethod( item, "passed", null ) ).booleanValue();
if( !itemPassed ) {
result += "Error [" + id + "]: " + ScriptableObject.getProperty( item, "message" ) + "\n";
}
}
return result;
}
private Suite createSuite( ScriptableObject suite ) {
if( suite == null ) {
return null;
}
String description = ( String )suite.get( "description" );
ScriptableObject parent = ( ScriptableObject )suite.get( "parentSuite" );
return new SuiteImpl( createSuite( parent ), description );
}
}
public class TestUtil {
public Object getResource( String name ) {
return resources.get( name );
}
}
}