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,17 +3,17 @@ 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;
public JasmineSpecFailureException(NativeObject specResultItem) { public JasmineSpecFailureException(NativeObject specResultItem) {
this.trace = (ScriptableObject) specResultItem.get("trace", specResultItem); this.trace = (ScriptableObject) specResultItem.get("trace", specResultItem);
} }
@Override @Override
public String getMessage() { public String getMessage() {
return (String) trace.get("message", trace); return (String) trace.get("message", trace);
} }
} }

View File

@ -5,73 +5,73 @@ 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-->"),
SOURCE_FILES_TO_INCLUDE("<!--SourceFileIncludes-->"), SOURCE_FILES_TO_INCLUDE("<!--SourceFileIncludes-->"),
SPEC_FILES_TO_INCLUDE("<!--SpecFileIncludes-->"); SPEC_FILES_TO_INCLUDE("<!--SpecFileIncludes-->");
private final String placeholder; private final String placeholder;
private TemplatePlaceholders(String placeholder) { private TemplatePlaceholders(String placeholder) {
this.placeholder = placeholder; this.placeholder = placeholder;
} }
public String getPlaceholder() { public String getPlaceholder() {
return placeholder; return placeholder;
} }
} }
private final JasmineSuite suite; private final JasmineSuite suite;
private final String[] jasmineSpecs; private final String[] jasmineSpecs;
private final String outputPath; private final String outputPath;
private final String outputFileName; private final String outputFileName;
public JasmineSpecRunnerGenerator(String[] jasmineSpecs, JasmineSuite suite, String outputPath, String outputFileName) { public JasmineSpecRunnerGenerator(String[] jasmineSpecs, JasmineSuite suite, String outputPath, String outputFileName) {
this.jasmineSpecs = jasmineSpecs; this.jasmineSpecs = jasmineSpecs;
this.suite = suite; this.suite = suite;
this.outputPath = outputPath; this.outputPath = outputPath;
this.outputFileName = outputFileName; this.outputFileName = outputFileName;
} }
public void generate() { public void generate() {
// TODO hardcoded relative path stuff wat configureerbaar maken // TODO hardcoded relative path stuff wat configureerbaar maken
String template = loadTemplate(); String template = loadTemplate();
template = replaceRelativePathsForLibs(template); template = replaceRelativePathsForLibs(template);
template = template.replaceAll(TemplatePlaceholders.SOURCE_FILES_TO_INCLUDE.getPlaceholder(), template = template.replaceAll(TemplatePlaceholders.SOURCE_FILES_TO_INCLUDE.getPlaceholder(),
getJavascriptFileIncludes("./../../../main/webapp/js", suite.sources())); getJavascriptFileIncludes("./../../../main/webapp/js", suite.sources()));
template = template.replaceAll(TemplatePlaceholders.SPEC_FILES_TO_INCLUDE.getPlaceholder(), template = template.replaceAll(TemplatePlaceholders.SPEC_FILES_TO_INCLUDE.getPlaceholder(),
getJavascriptFileIncludes("./../specs", jasmineSpecs)); getJavascriptFileIncludes("./../specs", jasmineSpecs));
try { try {
FileUtils.writeStringToFile(new File(outputPath + "/" + outputFileName), template); FileUtils.writeStringToFile(new File(outputPath + "/" + outputFileName), template);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("unable to write spec runner contents to destination", e); throw new RuntimeException("unable to write spec runner contents to destination", e);
} }
} }
private String replaceRelativePathsForLibs(String template) { private String replaceRelativePathsForLibs(String template) {
return template.replaceAll(TemplatePlaceholders.RELATIVE_PATH.getPlaceholder(), suite.jsRootDir()); return template.replaceAll(TemplatePlaceholders.RELATIVE_PATH.getPlaceholder(), suite.jsRootDir());
} }
private String getJavascriptFileIncludes(String path, String[] jsFiles) { private String getJavascriptFileIncludes(String path, String[] jsFiles) {
StringBuilder sourceFileIncludes = new StringBuilder(); StringBuilder sourceFileIncludes = new StringBuilder();
for (String sourceFile : jsFiles) { for (String sourceFile : jsFiles) {
sourceFileIncludes.append("\t\t<script type='text/javascript' src='" + path + "/" + sourceFile sourceFileIncludes.append("\t\t<script type='text/javascript' src='" + path + "/" + sourceFile
+ "'></script>\r\n"); + "'></script>\r\n");
} }
return sourceFileIncludes.toString(); return sourceFileIncludes.toString();
} }
private String loadTemplate() { private String loadTemplate() {
String template = null; String template = null;
try { try {
template = FileUtils.readFileToString(new File(suite.jsRootDir() + "/lib/specRunner.tpl")); template = FileUtils.readFileToString(new File(suite.jsRootDir() + "/lib/specRunner.tpl"));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("spec runner template file not found!", e); throw new RuntimeException("spec runner template file not found!", e);
} }
return template; return template;
} }
} }

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,150 +5,132 @@ 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 {
private Context jsContext; private Context jsContext;
private Scriptable jsScope; private Scriptable jsScope;
public RhinoContext() { public RhinoContext() {
this.jsContext = createJavascriptContext(); this.jsContext = createJavascriptContext();
this.jsScope = createJavascriptScopeForContext(this.jsContext); this.jsScope = createJavascriptScopeForContext(this.jsContext);
} }
public RhinoContext(Scriptable sharedScope) { public RhinoContext(Scriptable sharedScope) {
this.jsContext = createJavascriptContext(); this.jsContext = createJavascriptContext();
Scriptable newScope = this.jsContext.newObject(sharedScope); Scriptable newScope = this.jsContext.newObject(sharedScope);
newScope.setPrototype(sharedScope); newScope.setPrototype(sharedScope);
newScope.setParentScope(null); newScope.setParentScope(null);
this.jsScope = newScope; this.jsScope = newScope;
} }
private RhinoContext createNewRhinoContextBasedOnPrevious() { private RhinoContext createNewRhinoContextBasedOnPrevious() {
return new RhinoContext(this.jsScope); return new RhinoContext(this.jsScope);
} }
public void runAsync(final RhinoRunnable runnable) { public void runAsync(final RhinoRunnable runnable) {
new Thread(new Runnable() { new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
RhinoContext newRhinoContextBasedOnPrevious = createNewRhinoContextBasedOnPrevious(); RhinoContext newRhinoContextBasedOnPrevious = createNewRhinoContextBasedOnPrevious();
try { try {
runnable.run(newRhinoContextBasedOnPrevious); runnable.run(newRhinoContextBasedOnPrevious);
} finally { } finally {
newRhinoContextBasedOnPrevious.exit(); newRhinoContextBasedOnPrevious.exit();
} }
} }
}).start(); }).start();
} }
public Object evalJS(String js) { public Object evalJS(String js) {
return this.jsContext.evaluateString(this.jsScope, js, "script", 1, null); return this.jsContext.evaluateString(this.jsScope, js, "script", 1, null);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends ScriptableObject> T createClassInJS(Class<T> classToExport) { public <T extends ScriptableObject> T createClassInJS(Class<T> classToExport) {
exportClass(classToExport); exportClass(classToExport);
T newObj = (T) jsContext.newObject(jsScope, classToExport.getSimpleName()); T newObj = (T) jsContext.newObject(jsScope, classToExport.getSimpleName());
return newObj; return newObj;
} }
public void setProperty(String objectToReceiveProperty, String property, Object value) { public void setProperty(String objectToReceiveProperty, String property, Object value) {
Object obj = evalJS(objectToReceiveProperty); Object obj = evalJS(objectToReceiveProperty);
if (obj == null || !(obj instanceof ScriptableObject)) { if (obj == null || !(obj instanceof ScriptableObject)) {
throw new IllegalStateException("object to receive property is no ScriptableObject but a " throw new IllegalStateException("object to receive property is no ScriptableObject but a "
+ (obj == null ? "" : obj.getClass().getSimpleName())); + (obj == null ? "" : obj.getClass().getSimpleName()));
} }
ScriptableObject objectToReceive = (ScriptableObject) obj; ScriptableObject objectToReceive = (ScriptableObject) obj;
objectToReceive.put(property, objectToReceive, value); objectToReceive.put(property, objectToReceive, value);
} }
private void exportClass(Class<? extends ScriptableObject> classToExport) { private void exportClass(Class<? extends ScriptableObject> classToExport) {
try { try {
ScriptableObject.defineClass(this.jsScope, classToExport); ScriptableObject.defineClass(this.jsScope, classToExport);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void load(String path, String... jsFiles) { public void load(String path, String... jsFiles) {
for (String jsFile : jsFiles) { for (String jsFile : jsFiles) {
load(path + jsFile); load(path + jsFile);
} }
} }
public void load(String fileName) { public void load(String fileName) {
evalJS("load('" + fileName + "')"); evalJS("load('" + fileName + "')");
// Main.processFile(this.jsContext, this.jsScope, fileName); // Main.processFile(this.jsContext, this.jsScope, fileName);
} }
public Object executeFunction(ScriptableObject object, String fnName, Object[] arguments) { public Object executeFunction(ScriptableObject object, String fnName, Object[] arguments) {
Object fnPointer = object.get(fnName, object); Object fnPointer = object.get(fnName, object);
if (fnPointer == null || !(fnPointer instanceof Function)) { if (fnPointer == null || !(fnPointer instanceof Function)) {
fnPointer = object.getPrototype().get(fnName, object); fnPointer = object.getPrototype().get(fnName, object);
} }
return ((Function) fnPointer).call(jsContext, jsScope, object, arguments); return ((Function) fnPointer).call(jsContext, jsScope, object, arguments);
} }
public Object executeFunction(ScriptableObject object, String fnName) { public Object executeFunction(ScriptableObject object, String fnName) {
return executeFunction(object, fnName, new Object[] {}); return executeFunction(object, fnName, new Object[] {});
} }
public Context getJsContext() { public Context getJsContext() {
return jsContext; return jsContext;
} }
public Scriptable getJsScope() { public Scriptable getJsScope() {
return jsScope; return jsScope;
} }
public void loadEnv(String jsDir) { public void loadEnv(String jsDir) {
// TODO ensure rhino 1.7R3 instead of R2 -> geen shim nodig + paths // TODO ensure rhino 1.7R3 instead of R2 -> geen shim nodig + paths
// gedoe in orde zetten hier // gedoe in orde zetten hier
load(jsDir + "/lib/es5-shim-0.0.4.min.js"); load(jsDir + "/lib/es5-shim-0.0.4.min.js");
load(jsDir + "/lib/env.rhino.1.2.js"); load(jsDir + "/lib/env.rhino.1.2.js");
load(jsDir + "/lib/env.utils.js"); load(jsDir + "/lib/env.utils.js");
load(jsDir + "/envJsOptions.js"); load(jsDir + "/envJsOptions.js");
} }
private Global createJavascriptScopeForContext(Context jsContext) { private Global createJavascriptScopeForContext(Context jsContext) {
Global scope = new Global(); Global scope = new Global();
scope.init(jsContext); scope.init(jsContext);
return scope; return scope;
} }
private Context createJavascriptContext() { private Context createJavascriptContext() {
Context jsContext = ContextFactory.getGlobal().enterContext(); Context jsContext = ContextFactory.getGlobal().enterContext();
jsContext.setOptimizationLevel(-1); jsContext.setOptimizationLevel(-1);
jsContext.setLanguageVersion(Context.VERSION_1_5); // TODO 1.8 plx jsContext.setLanguageVersion(Context.VERSION_1_5); // TODO 1.8 plx
jsContext.setErrorReporter(new ChainedErrorReporter(jsContext.getErrorReporter())); jsContext.setErrorReporter(new ChainedErrorReporter(jsContext.getErrorReporter()));
return jsContext; return jsContext;
} }
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

@ -29,4 +29,11 @@ describe("root", function() {
}); });
}); });
describe("root2", function() {
it("root2Test", function() {
});
});