Merge branch 'master', remote-tracking branch 'origin/master'

This commit is contained in:
Wouter Groeneveld 2011-06-28 22:02:38 +02:00
commit dde8c0d89b
17 changed files with 9270 additions and 269 deletions

109
README.md
View File

@ -2,6 +2,37 @@
## What's this? ## What's this?
Something like this:
```javascript
describe("pure awesomeness", function() {
it("should be amazing!", function() {
expect(stuff).toEqual("amazing");
});
it("should be awesome", function() {
expect(moreStuff).toBe("awesome");
});
});
describe("coolness", function() {
it("should be cooler than ice when freezed", function() {
var coolness = CoolingRepository.beCool();
coolness.freeze();
expect(coolness.coolnessRatio).toBe(-100);
});
it("should be cool enough by default", function() {
expect(CoolingRepository.beCool().coolnessRatio).toBe(-5);
});
});
```
Being translated into something like this:
![Junit Eclipse runner](http://i54.tinypic.com/rswjrl.jpg)
* * *
Quite simple, it's a custsom Java Junit Runner that allows you to embed Javascript Unit tests (using Jasmine) in your Java-based projects. It fully integrates with your most beloved IDE, your most hated version control system and of course your most needed CI env. Quite simple, it's a custsom Java Junit Runner that allows you to embed Javascript Unit tests (using Jasmine) in your Java-based projects. It fully integrates with your most beloved IDE, your most hated version control system and of course your most needed CI env.
So let's rephrase: So let's rephrase:
@ -151,81 +182,3 @@ When the debug mode flag has been set to _true_, you can use the <a href="http:/
After pressing "GO", the tests will run and you can inspect stuff and step through the code. After pressing "GO", the tests will run and you can inspect stuff and step through the code.
Integrated debugging into for example Eclipse does not work for the moment. Integrated debugging into for example Eclipse does not work for the moment.
* * *
# Advanced: Implementation details
## RhinoContext API
The _RhinoContext_ class is basically a wrapper/facade/whatever which allows you to easily manipulate the Javascript scope.
Read <a href="https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts" target="_blank">Rhino docs: scopes and contexts</a> first please!
Creating a new RhinoContext initializes one "root" scope (toplevel), and assignes one context to the current Thread.
### Evaluating async javascript code
Creating another RhinoContext while passing the root scope, uses prototypal inheritance to create a new toplevel scope. This means the root scope is shared across different contexts (and thus different threads).
You can execute the _runAsync_ method, which does this:
* create a new thread and thus a new context
* create a new scope based on the root one -> shared
* execute stuff in the new scope (You can access root JS functions but not modify them, remember prototypal inheritance!)
* cleanup
For example, JasmineSpec uses the _execute_ Jasmine JS function on a spec and calls it in another thread:
```java
baseContext.runAsync(new RhinoRunnable() {
@Override
public void run(RhinoContext context) {
// get some random spec from Jasmine
NativeObject someSpec = (NativeObject) context.evalJS("jasmine.getEnv().currentRunner().suites()[0].specs()[0]");
context.executeFunction(someSpec, "execute");
}
});
```
### Creating a Rhino debugger
Basically creates a _org.mozilla.javascript.tools.debugger.Main_ object. Pitfall: create before loading all required JS files, but after creating the rhino context!
To acutally break once (so users can set breakpoints and press GO), use this:
> debugger.doBreak();
### Executing functions
_executeFunction_ is a convenience method to call a function on a passed NativeObject. The function pointer may reside in the object's prototype, you don't need to explicitly check this in Javascript but you do using Rhino!
## Envjs Utils/Hacks
### Error.stack fix
In firefox, you can get a stacktrace from a JS exception using:
> new Error("BOOM").stack
Of course this does not work in Envjs. But Rhino attaches an internal _rhinoException_ to each JS Error object, so using a bit of magic, now it's possible to call _getStackTrace()_
### Envjs.uri Windows relative paths fix
Use _file:///_ (three forward slashes) if no context has been provided. Works like this:
```javascript
Envjs.uri(path, "file:///" + ("" + Envjs.getcwd()).replace(/\\/g, '/') + "/")
```
### window.setTimeout fix
Used by Jasmine internally for async spec execution, but for some reason the Envjs Javascript implementation is broken.
A simple fix is possible, since using Rhino you can call Java objects in Javascript space! Wow awesome. So just create a new thread and use _sleep_:
```javascript
window.setTimeout = function(closure, timeout) {
spawn(function() {
java.lang.Thread.sleep(timeout);
closure();
});
};
```

9
bin/bootstrap.js vendored Executable file
View File

@ -0,0 +1,9 @@
load("./../src/test/javascript/lib/env.rhino.1.2.js");
load("./../src/test/javascript/lib/env.utils.js");
load("./../src/test/javascript/envJsOptions.js");
load("jquery-1.6.1.js");
function append(itm) {
$('body').append($(itm));
}

8936
bin/jquery-1.6.1.js vendored Executable file

File diff suppressed because it is too large Load Diff

BIN
bin/js.jar Executable file

Binary file not shown.

7
bin/rhino.bat Executable file
View File

@ -0,0 +1,7 @@
@echo off
REM -opt -1 is needed for Envjs (interpretation, not compilation mode)
REM Rhino JAR options: See https://developer.mozilla.org/en/Rhino_Shell
REM EnvJS usage: See http://www.envjs.com/doc/guides#running-rhino
java -cp js.jar org.mozilla.javascript.tools.shell.Main -opt -1

2
bin/rhino.debug.bat Executable file
View File

@ -0,0 +1,2 @@
@echo off
java -cp js.jar org.mozilla.javascript.tools.debugger.Main -opt -1

BIN
jasmine-junit-runner.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -10,7 +10,7 @@ import org.mozilla.javascript.NativeObject;
import be.klak.rhino.RhinoContext; import be.klak.rhino.RhinoContext;
public class JasmineJSSuiteConverter { class JasmineJSSuiteConverter {
private final RhinoContext context; private final RhinoContext context;
@ -36,7 +36,7 @@ public class JasmineJSSuiteConverter {
specs.addAll(convertToJunitDescription(suite, suiteDescription)); specs.addAll(convertToJunitDescription(suite, suiteDescription));
NativeArray subSuites = (NativeArray) context.executeFunction(suite, "suites"); NativeArray subSuites = (NativeArray) context.executeFunction(suite, "suites");
convertSuiteArrayToDescriptions(subSuites, suiteDescription, processed); specs.addAll(convertSuiteArrayToDescriptions(subSuites, suiteDescription, processed));
} }
} }

View File

@ -3,7 +3,7 @@ package be.klak.junit.jasmine;
import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
public class JasmineSpecFailureException extends Exception { class JasmineSpecFailureException extends Exception {
private final ScriptableObject trace; private final ScriptableObject trace;

View File

@ -5,7 +5,7 @@ import java.io.IOException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
public class JasmineSpecRunnerGenerator { class JasmineSpecRunnerGenerator {
private enum TemplatePlaceholders { private enum TemplatePlaceholders {
RELATIVE_PATH("<!--RelativePath-->"), RELATIVE_PATH("<!--RelativePath-->"),

View File

@ -9,6 +9,7 @@ import org.junit.Before;
import org.junit.runner.Description; import org.junit.runner.Description;
import org.junit.runner.Runner; import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.RunNotifier;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.tools.debugger.Main; import org.mozilla.javascript.tools.debugger.Main;
@ -35,7 +36,7 @@ public class JasmineTestRunner extends Runner {
Main debugger = null; Main debugger = null;
if (this.suiteAnnotation.debug()) { if (this.suiteAnnotation.debug()) {
debugger = this.rhinoContext.createDebugger(); debugger = createDebugger();
} }
this.rhinoContext = setUpRhinoScope(); this.rhinoContext = setUpRhinoScope();
@ -62,6 +63,23 @@ public class JasmineTestRunner extends Runner {
context.evalJS("jasmine.getEnv().addReporter(new jasmine.DelegatorJUnitReporter());"); 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() { private JasmineSuite getJasmineSuiteAnnotationFromTestClass() {
JasmineSuite suiteAnnotation = testClass.getAnnotation(JasmineSuite.class); JasmineSuite suiteAnnotation = testClass.getAnnotation(JasmineSuite.class);
if (suiteAnnotation == null) { if (suiteAnnotation == null) {

View File

@ -5,7 +5,6 @@ import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function; import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.tools.debugger.Main;
import org.mozilla.javascript.tools.shell.Global; import org.mozilla.javascript.tools.shell.Global;
public class RhinoContext { public class RhinoContext {
@ -134,21 +133,4 @@ public class RhinoContext {
public void exit() { public void exit() {
Context.exit(); Context.exit();
} }
public Main createDebugger() {
Main debugger = new Main("JS Rhino 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;
}
} }

12
src/test/java/be/klak/env/EnvUtilsTest.java vendored Executable file
View File

@ -0,0 +1,12 @@
package be.klak.env;
import org.junit.runner.RunWith;
import be.klak.junit.jasmine.JasmineSuite;
import be.klak.junit.jasmine.JasmineTestRunner;
@RunWith(JasmineTestRunner.class)
@JasmineSuite
public class EnvUtilsTest {
}

View File

@ -16,7 +16,7 @@ public class DescriptionsRecursiveTreeInRunnerTest {
Description baseTestDescription = new JasmineTestRunner(RecursiveTreeTest.class).getDescription(); Description baseTestDescription = new JasmineTestRunner(RecursiveTreeTest.class).getDescription();
assertThat(baseTestDescription.getDisplayName()).contains(RecursiveTreeTest.class.getSimpleName()); assertThat(baseTestDescription.getDisplayName()).contains(RecursiveTreeTest.class.getSimpleName());
assertThat(baseTestDescription.getChildren()).hasSize(1); assertThat(baseTestDescription.getChildren()).hasSize(2);
Description root = baseTestDescription.getChildren().get(0); Description root = baseTestDescription.getChildren().get(0);
assertThat(root.getDisplayName()).isEqualTo("root"); assertThat(root.getDisplayName()).isEqualTo("root");
assertThat(root.getChildren()).hasSize(3); assertThat(root.getChildren()).hasSize(3);
@ -24,6 +24,12 @@ public class DescriptionsRecursiveTreeInRunnerTest {
assertThat(root.getChildren().get(0).getDisplayName()).isEqualTo("rootTest"); assertThat(root.getChildren().get(0).getDisplayName()).isEqualTo("rootTest");
assertChild1AndChildren(root); assertChild1AndChildren(root);
assertChild2AndChildren(root); assertChild2AndChildren(root);
Description root2 = baseTestDescription.getChildren().get(1);
assertThat(root2.getDisplayName()).isEqualTo("root2");
assertThat(root2.getChildren()).hasSize(1);
assertThat(root2.getChildren().get(0).getDisplayName()).isEqualTo("root2Test");
} }
private void assertChild2AndChildren(Description root) { private void assertChild2AndChildren(Description root) {

View File

@ -55,6 +55,7 @@
* Envjs specific hacks * Envjs specific hacks
* 1) Fix Envjs relative path system to work with Windows path systems * 1) Fix Envjs relative path system to work with Windows path systems
* 2) Fix window.setTimeout() using Rhino specific functions * 2) Fix window.setTimeout() using Rhino specific functions
* 3) Fix CSS2Properties support: all properties have the same objmaps, wtf?
*/ */
(function() { (function() {
@ -73,4 +74,17 @@
}); });
}; };
(function(css) {
var setCssProperty = css.prototype.setProperty;
css.prototype.setProperty = function(name, value) {
// create a shallow clone of __supportedStyles__ (styleIndex' default value) if prototype not yet set
if(Object.keys(Object.getPrototypeOf(this.styleIndex)).length === 0) {
this.styleIndex = Object.create(this.styleIndex);
}
return setCssProperty.call(this, name, value);
}
})(CSS2Properties);
})(); })();

View File

@ -0,0 +1,55 @@
describe("envjs fixes", function() {
describe("CSS2 style property support", function() {
var someColor = "#FFFFFF";
var someFont = "12px 'Bitstream Vera Sans Mono','Courier',monospace";
it("should be visible and displayed by default for all new elements", function() {
var elStyle = document.createElement("b").style;
expect(elStyle.display).toBeFalsy();
expect(elStyle.visibility).toBeFalsy();
});
it("should be able to set a style value through setters", function() {
var someB = document.createElement("b");
someB.style.color = someColor;
expect(someB.style.color).toBe(someColor);
});
it("should have unique style values per DOM element", function() {
var someEl1 = document.createElement("b");
var someEl2 = document.createElement("b");
someEl1.style.color = someColor;
someEl2.style.font = someFont;
expect(someEl1.style.font).toBeFalsy();
expect(someEl2.style.color).toBeFalsy();
});
});
describe("window setTimeout", function() {
it("should wait one second before executing", function() {
var done = false;
window.setTimeout(function() {
done = true;
}, 1000);
waitsFor(function() {
return done === true;
});
runs(function() {
expect(done).toBeTruthy();
});
});
});
});

View File

@ -30,3 +30,10 @@ describe("root", function() {
}); });
}); });
describe("root2", function() {
it("root2Test", function() {
});
});