Package be.klak.junit.jasmine

Source Code of be.klak.junit.jasmine.JasmineTestRunner$DefaultSuite

package be.klak.junit.jasmine;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.apache.commons.lang.StringUtils;

import java.net.URL;

import org.junit.After;
import org.junit.Before;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.tools.debugger.Main;

import be.klak.rhino.RhinoContext;

public class JasmineTestRunner extends Runner {

  private static final int SLEEP_TIME_MILISECONDS = 50;

  // relative to classpath
  private static final String JASMINE_LIB_DIR = "js/lib/jasmine-1.3.1";

  private JasmineDescriptions jasmineSuite;

  protected final RhinoContext rhinoContext;
  protected final JasmineSuite suiteAnnotation;
  private final Class<?> testClass;

  @JasmineSuite
  private class DefaultSuite { }

  public JasmineTestRunner(Class<?> testClass) {
    this.testClass = testClass;
    this.suiteAnnotation = getJasmineSuiteAnnotationFromTestClass();

    Main debugger = null;
    if (this.suiteAnnotation.debug()) {
      debugger = createDebugger();
    }

    this.rhinoContext = setUpRhinoScope();

    if (this.suiteAnnotation.debug()) {
      debugger.doBreak();
    }
  }

  private RhinoContext setUpRhinoScope() {
    RhinoContext context = new RhinoContext();

        pre(context);

        if (suiteAnnotation.envJs()) {
      context.loadEnv(suiteAnnotation.jsRootDir());
    } else {
      context.load(suiteAnnotation.jsRootDir(), "/lib/no-env.js");
    }

    setUpJasmine(context);

    context.load(suiteAnnotation.sourcesRootDir() + "/", suiteAnnotation.sources());
    context.load(suiteAnnotation.jsRootDir() + "/specs/", getJasmineSpecs(suiteAnnotation));

    return context;
  }

    protected void pre(RhinoContext context) { }

  private void setUpJasmine(RhinoContext context) {
    context.loadFromClasspath(JASMINE_LIB_DIR + "/jasmine.js");
    context.loadFromClasspath(JASMINE_LIB_DIR + "/jasmine.delegator_reporter.js");

    context.evalJS("jasmine.getEnv().addReporter(new jasmine.DelegatorJUnitReporter());");
  }

  private Main createDebugger() {
    Main debugger = new Main("JS Debugger");

    debugger.setExitAction(new Runnable() {
      public void run() {
        System.exit(0);
      }
    });

    debugger.attachTo(ContextFactory.getGlobal());
    debugger.pack();
    debugger.setSize(600, 460);
    debugger.setVisible(true);

    return debugger;
  }

  private JasmineSuite getJasmineSuiteAnnotationFromTestClass() {
    JasmineSuite suiteAnnotation = testClass.getAnnotation(JasmineSuite.class);
    if (suiteAnnotation == null) {
      suiteAnnotation = DefaultSuite.class.getAnnotation(JasmineSuite.class);
    }
    return suiteAnnotation;
  }

  private String[] getJasmineSpecs(JasmineSuite suiteAnnotation) {
    if (suiteAnnotation.specs().length == 0) {
      return new String[] { StringUtils.uncapitalize(testClass.getSimpleName()).replace("Test", "Spec") + ".js" };
    }
    return suiteAnnotation.specs();
  }

  private void resetEnvjsWindowSpace() {
    URL blankUrl = Thread
      .currentThread()
      .getContextClassLoader()
      .getResource("js/lib/blank.html");

    if (blankUrl == null) {
      throw new IllegalStateException("Unable to load js/lib/blank.html from classpath");
    }

    String blankUrlStr = blankUrl.toExternalForm();

    // "file:/path/to/file" is not legal, but "file:///path/to/file" is
    if (blankUrlStr.startsWith("file:/") && (! blankUrlStr.startsWith("file:///"))) {
      blankUrlStr = "file://" + blankUrlStr.substring(5);
    }

    this.rhinoContext.evalJS(String.format(
      "window.location = '%s';",
      blankUrlStr
    ));
  }

  private JasmineDescriptions getJasmineDescriptions() {
    if (this.jasmineSuite == null) {
      NativeArray baseSuites = (NativeArray) rhinoContext.evalJS("jasmine.getEnv().currentRunner().suites()");
      this.jasmineSuite = new JasmineJSSuiteConverter(rhinoContext).convertToJunitDescriptions(testClass, baseSuites);
    }
    return this.jasmineSuite;
  }

  @Override
  public Description getDescription() {
    return getJasmineDescriptions().getRootDescription();
  }

  @Override
  public void run(RunNotifier notifier) {
    generateSpecRunnerIfNeeded();

    for (JasmineSpec spec : getJasmineDescriptions().getSpecs()) {
      Object testClassInstance = createTestClassInstance();
      fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, Before.class);

      try {
        notifier.fireTestStarted(spec.getDescription());
        spec.execute(rhinoContext);
        while (!spec.isDone()) {
          waitALittle();
        }

        reportSpecResultToNotifier(notifier, spec);

        if (suiteAnnotation.envJs()) {
          resetEnvjsWindowSpace();
        }
      } finally {
        fireMethodsWithSpecifiedAnnotationIfAny(testClassInstance, After.class);
      }
    }

        after();
  }

    protected void after() {
    this.rhinoContext.exit();
    }

  private Object createTestClassInstance() {
    try {
      return testClass.newInstance();
    } catch (Exception ex) {
      throw new RuntimeException("Unable to create a new instance of testClass " + testClass.getSimpleName()
          + " using a no-arg constructor", ex);
    }
  }

  private void fireMethodsWithSpecifiedAnnotationIfAny(Object testClassInstance, Class<? extends Annotation> annotation) {
    for (Method method : testClass.getMethods()) {

      try {
        if (method.getAnnotation(annotation) != null) {
          method.setAccessible(true);
          Class<?>[] parameterTypes = method.getParameterTypes();
          if (parameterTypes.length == 0) {
            method.invoke(testClassInstance, (Object[]) null);
          } else if (parameterTypes.length == 1 && RhinoContext.class.isAssignableFrom(parameterTypes[0])) {
            method.invoke(testClassInstance, new Object[] { this.rhinoContext });
          } else {
            throw new IllegalStateException("Annotated method does not have zero or rhinoContext as parameterTypes");
          }
        }
      } catch (Exception ex) {
        throw new RuntimeException(
            "Exception while firing " + annotation.getSimpleName() + " method: " + method.getName(), ex);
      }
    }
  }

  private void generateSpecRunnerIfNeeded() {
    if (suiteAnnotation.generateSpecRunner()) {
      String[] jasmineSpecs = getJasmineSpecs(suiteAnnotation);
      StringBuffer outputPath = new StringBuffer(suiteAnnotation.jsRootDir()).append("/runners");
      if (StringUtils.isNotBlank(suiteAnnotation.specRunnerSubDir())) {
        outputPath.append('/').append(suiteAnnotation.specRunnerSubDir());
      }
      new JasmineSpecRunnerGenerator(jasmineSpecs, suiteAnnotation, outputPath.toString(),
          testClass.getSimpleName()
              + "Runner.html")
          .generate();
    }
  }

  private void reportSpecResultToNotifier(RunNotifier notifier, JasmineSpec spec) {
    if (spec.isPassed(rhinoContext)) {
      notifier.fireTestFinished(spec.getDescription());
    } else if (spec.isFailed(rhinoContext)) {
      notifier.fireTestFailure(spec.getJunitFailure(rhinoContext));
    } else {
      throw new IllegalStateException("Unexpected spec status received: " + spec);
    }
  }

  private void waitALittle() {
    try {
      Thread.sleep(SLEEP_TIME_MILISECONDS);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

}
TOP

Related Classes of be.klak.junit.jasmine.JasmineTestRunner$DefaultSuite

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.