Documentation
Maven plugin
You can activate separately the JavaScript generation for main sources and tests. Here are the goals of the maven plugin:
- generate - activate the generation for the main sources (in the "process-sources" phase)
- generate-test - activate the generation for the test sources (in the "process-test-classes" phase)
- copy-js - copies the JavaScript files (either generated or just bridged) from the dependencies to the final artifact (war file), so that they can be used in the web pages
- includes/include - the path specifier describing for what Java source you want to generate JavaScript. It's the standard path Maven/Ant specifiers. Defaults to **.
- excludes/excludes - the path specifier describing what Java source you want to exclude from the JavaScript generate. It's the standard path Maven/Ant specifiers. Defaults to "" (nothing).
- allowedPackages - it's a list of Java packages that are allowed to be used inside the Java sources used for generation. A common usage is when you reserved in the Java sources a package for bridges to some JavaScript libraries. This package should than be excluded from the generation process.
- generateArrayHasOwnProperty - true to generate inside each array iteration if (!array.hasOwnProperty(index)) continue; in order to protect array iteration from the inclusion of the methods added to Array's prototype. Default value if true
- pack - if true, a single JavaScript will be created containing all the generated JavaScript files in a correct order (follows the dependencies)
Special methods and constructions
Most of the Java code you write will be translated basically as-is to its JavaScript counterpart (following some basic rules like for example no type accompanies variables, fields or parameters declarations). But there are cases where JavaScript constructions could not have a Java counterpart. In all cases where a method starts with the dollar sign ($) you should expect a generated JavaScript code that looks slighty different than the corresponding Java construction. Here is the list of these constructions and methods (Note: in the basic usage they apply to very specific classes (like Map). But for these methodes, STJS does not check the owner type also, so you can use it with your own types, if you see fit):
Method | Java | JavaScript | Description |
---|---|---|---|
JSCollections.$map | $map(k1,v1,k2,v2) | {k1:v1, k2:v2} | Create a map with the given keys and values |
Map.$put | x.$put(a, b) | x[a] = b | Put a key and a value in a map |
Map.$get | x.$get(a) | x[a] | Retrieve a value from a map |
Map.$delete | x.$delete(a) | delete x[a] | Delete an entry from a map |
JSCollections.$array | $array(a, b, c) | [a, b, c] | Create an array with the given items |
JSCollections.$castArray | Array<T> a = $castArray(T[]b) | var a=b | See a java array as the org.stjs.javascript.Array type |
Array.$get | x.$get(a) | x[a] | Retrieve an item from an array |
Array.$set | x.$set(index, val) | x[index] = val | Modify an item in an array |
Global.$or | $or(a,b,c) | a || b || c | Return first value the is equivalent to true in JavaScript |
JsObjectAdapter.$properties | Map<String,Object> map = $properties(obj) | var map = obj | see an object as a map of its properties |
JsObjectAdapter.$object | T obj = $object(map) | var obj = map | see map as an object of a given type with the same properties |
JsObjectAdapter.$prototype | $prototype(obj) | obj.prototype | Return the prototype of a JavaScript object |
JsObjectAdapter.$constructor | $constructor(obj) | obj.constructor | Return the constructor of a JavaScript object |
JsObjectAdapter.$js | $js(javascriptCode) | javascriptCode | Replaces with the given javascriptCode (only String literals accepted). Use it only in extreme cases as it makes it impossible to minimize the code. |
var A = { field: null, method: function(){ } }and you'd like to create an STJS bridge (so that you can use it in Java) and you want to do it as Java interface (you can also do it as a normal class). As you cannot have:
public interface A { public String field; public String method(); }what you can do is:
public interface A{ public String $field(); public void $field(String s); //don't add, if the field is read-only public String method(); }The code a.$field("abc") will be correctly translated to a.field = "abc"
If you wanted, for example to keep the call to the field method as is, you could've used the @Template("none") annotation on the method, like the following code:
public interface A{ public String $field(); @Template("none") public void $field(String s); //don't add, if the field is read-only public String method(); }So now the code a.$field("abc") will be translated to a.$field("abc")
In the next major version we'd like to use the @Template mechanism instead of the method's name in order to control how the code is generated.
Annotations
There are several annotations that can be used to control how the is generated.
- @SyntheticType - use this annotation in both the code for generation and in the bridges to specify types for which the instances are built with the standard object constructor - {} - rather than with type's constructor. For bridges this is useful when you want to provide a type where the bridged library didn't have one. An example is DialogOptions (for jQuery dialog plugin) that does not exist in the jQuery library, but it shows you the options supported by the plugin
- @STJSBridge - mark a type as STJS bridge, so it can be used in the code for generation.
- @Adapter - useful when writing bridges for existent JavaScript types. These are classes that are used to supply methods for Java types when they don't have a method that their JavaScript counterpart has. For example for Number in JavaScript you can do number.toFixed(2). As in Java this method does not exist and as the Java Number-derived classes are all final, the only alternative is to put this method in another class - an adapter class. All the methods of an adapter class must have their first parameter the object to which the method is applied. The other parameters are the parameters normally supplied to the JavaScript method.
- @GlobalScope - use this annotation in both the code for generation and in the bridges to specify types of which the static methods and fields are generated as belonging directly to the global JavaScript scope. An example is org.stjs.javascript.Global class that proposes the window global object and some other functions globally available in JavaScript
- @JavascriptFunction - use this annotation to mark an interface that is used to simulate JavaScript functions. When implementing inline one of those interfaces, the generated JavaScript code will be an anonymous function. Consequently whenever in the code the unique method of this interface is called, it will be generated a direct call to the function.
- @Namespace - use this annotation to add a namespace for a class or an entire package (annotation is on package-info class within the package). As ST-JS generates the name of a class regardless the package it belongs, if you want to avoid name clash between different classes you have to use namespaces. The name of classes having a namespace will always be generated together with the given namespace. (Please note that the package annotation is not recursive! So you to annotate sub-packages as well.)
- @Template - this annotation can be used on a method to control how the call to this method will be generated. The parameter taken by this annotation is a name of a defined template. Currently only @Template("none") is defined, that leaves a method call as-is. object.$get(i) for example will generate object.$get(i) instead of object[i].
STJS helper
The org.stjs.javascript.stjs.STJS interface is the bridge to the JavaScript code coming with the generator. It offers some helpers for different type inexistent in JavaScript:
- boolean isEnum(obj) - return true if the given JavaScript object is an enum entry (the JavaScript wrapper needed to allow the usage of Java enums)
- Exception exception(Object obj) - allows you in Java to use any object as an exception. At runtime, the executed JavaScript code will return exactly the received parameter
- public <T> T parseJSON(String json, Class<T> clazz) - parses a JSON string in a "type-safe" manner by creating the object hierarchy using the given type definition
JUnit runner configuration
The STJS JUnit runner runs your unit tests in one or multiple browsers of your chosing. To do so, it builds an HTML page that includes all the necessary HTML and javascript necessary to execute your tests and return the result to JUnit. The HTML page that will be sent to the browser includes the following things:
- The javascript version of all the classes that are being tested, as well as that of all classes they depend on.
- If any bridge class or interface is used, the javascript code that corresponds. For example, if the classes under test use jQuery 1.7.1, then jQuery-1.7.1.js is imported in the HTML page that is sent to the browser.
- If specified with the @ScriptsBefore or @ScriptsAfter annotations, a set of javascript files that are required for the tests to pass but that are not declared as a STJS bridge class or interface.
- If specified with the @HtmlFixture annotation, a fragment of HTML that is required for the tests to pass.
Before using the JUnit runner you have to add the dependency to your projects pom.xml:
<dependency> <groupId>org.st-js</groupId> <artifactId>test-helper</artifactId> <version>${stjs.version}</version> <scope>test</scope> </dependency>
To activate the STJS JUnit runner you have to annotate your JUnit test classes this way: @RunWith(STJSTestDriverRunner.class).
Besides the standard JUnit annotations such as @Test, @Before, @After, the STJSTestDriverRunner supports several other custom annotations.
-
@HtmlFixture is an optional annotation that can be applied to your test class, and is used to force the STJSTestDriverRunner to include a specific HTML fragment on the page. If the fragment is short enough, it can be specified verbatim in the annotation.
@RunWith(STJSTestDriverRunner.class) @HtmlFixture("<div id='importantContainer'></div>") public class TestSimpleFixture { ... }
If the fragment is longer the annotation can specify a path to a classpath resource that contains the HTML code. Then html fragment file will be looked up using ClassLoader.getResourceAsStream(). This means that STJSTestDriverRunner (trough the ClassLoader) will first attempt to find the file in your class path. If the file cannot be found in your classpath, then STJSTestDriverRunner will look for the file starting at the "document root" or your webapp, if any. For a typical maven project, this means it will first look in /target/WEB-INF/classes, and then in /target.
@RunWith(STJSTestDriverRunner.class) @HtmlFixture(url = "/SomeComplexHtmlFragment.html") public class TestSimpleFixture { ... }
-
@ScriptsBefore and @ScriptsAfter are optional annotations that can be applied to your test class, and are used to force the STJSTestDriverRunner to include extra javascript files in the <head> section of the HTML page that is sent to the browser. Script files specified in @ScriptsBefore will be included in the HTML page before the class under test and all its dependencies. Script files specifed in @ScriptsAfter will be included in the HTML page after the class under test and all its dependencies.
Each of the strings passed as a value to these annotations will be used verbatim in the generated HTML. This means that you can pass either a path to a javascript file that exists within your project, or one that resides on another domain.
@RunWith(STJSTestDriverRunner.class) @ScriptsBefore({"/someLibBefore.js"}) @ScriptsAfter({"http://example.com/someLibAfter.js"}) public class TestHelloWorld { ... }
Will generate the following fragment of HTML
<head> <!-- contents of @ScriptsBefore --> <script src="/someLibBefore.js" type="text/javascript"></script> <!-- Class under test plus other stuff required by stjs --> <script src="/stjs.js" type="text/javascript"></script> <script src="/HelloWorld.js" type="text/javascript"></script> <!-- contents of @ScriptsAfter --> <script src="http://example.com/someLibAfter.js" type="text/javascript"></script> </head>
If the script passed to this annotation is a reference to a file defined in your project (ie: it doesn't start with a protocol handler such as http:, file:, ftp:, https:, etc...), then the script file will be looked up using ClassLoader.getResourceAsStream(). This means that STJSTestDriverRunner (trough the ClassLoader) will first attempt to find the file in your class path. If the file cannot be found in your classpath, then STJSTestDriverRunner will look for the file starting at the "document root" or your webapp, if any. For a typical maven project, this means it will first look in /target/WEB-INF/classes, and then in /target.
You can configure some of the test driver's parameters in a file called stjs-test.properties that must be placed the root of your class path. The properties supported by this file are the following:
property | description |
---|---|
stjs.test.config | The classpath location of properties file that contains the stjs test driver configuration. Default value is "/stjs-test.properties" |
stjs.test.browsers | A comma separated list of browsers on which to run the tests. A test is successful only if it has been run successfully on all the browsers in this list. See the table below for a description of the supported browsers. The default value is "desktopDefault". |
stjs.test.port | the port opened by the test driver waiting for connection from the browsers. Default value is 8055 |
stjs.test.wait | the time (in seconds) the test driver waits for the number of configured browsers to connect. Default value is 10 |
stjs.test.skipIfNoBrowser | if this is true, if no browser was connected that it considers the tests as ignored (can be used in some batch processing, without failing completely the tests). Default value is false |
stjs.test.startBrowser | if true, if after 2 seconds (normally check of the client's code check is 1 second) no browser connected to the test driver, it tries to start the system's default browser. This can only work is the Desktop.isDesktopSupported() check return true (so usually a developer's machine). Default value is true |
stjs.test.testTimeout | the time (in seconds) the test driver waits for a test to return a result from the browser. Passed this time the test is considered failed. Default value is 2 seconds |
stjs.test.debug | if this is true, debug information is displayed. Default value is false |
firefox.bin | the path to the binary of firefox (see "firefox" in the table below) |
chrome.bin | the path to the binary of Google Chrome or chromium (see "chrome" in the table below) |
phantomjs.bin | the path to the binary of phantomjs (see "phantomjs" in the table below) |
All these properties can be overridden by specifying them as system properties when launching the java vm. For example to force debug mode when launching the tests via Maven you can run `mvn test -Dstjs.test.debug=true`.
The browsers that can be added to the stjs.test.browsers property are the following.
Browsers that require a graphics system | |
---|---|
Name | Description |
desktopDefault |
Launches whichever browser is the default for the desktop on which the tests are running. On windows the browser is launched using Desktop.getDesktop().browse(). On other systems it is launched using the "xdg-open" utility. This browser might fail to launch on headless machines in some operating systems, for example a linux server without an X11 server. |
firefox |
Launches firefox. STJS will look in the common firefox installation directories to find the binary. If firefox is not installed in the standard location on your target system, you can specify the path of the binary in the firefox.bin property in "stjs-test.properties", in whichever file you specified in the stjs.test.config system property or on the command line. |
chrome |
Launches Goole Chrome (or chromium). STJS will look in the common chrome installation directories to find the binary. If chrome is not installed in the standard location on your target system, you can specify the path of the binary in the chrome.bin property in "stjs-test.properties", in whichever file you specified in the stjs.test.config system property or on the command line. |
Browsers that can run on headless systems | |
Name | Description |
rhino |
Runs the tests in an instance of Rhino that is embedded in stjs' test runner. No additional software needs to be installed on the target system for this browser to run. |
remote |
Doesn't launch any browsers. It assumes that a browser is already running somewhere (potentially on a remote machine), and that this browsers periodically polls the stjs test runners HTTP port to fetch unit tests to run. |
phantomjs |
Launches all the tests in phantomjs. Phantomjs is a lightweight, headless browser based on webkit and V8. Phantomjs needs to be installed on the target system for this browser to run. STJS will look in the common phantomjs installation directories to find the binary. If phantomjs is not installed in the standard location on your target system, you can specify the path of the binary in the phantomjs.bin property in "stjs-test.properties", in whichever file you specified in the stjs.test.config system property or on the command line. |
headlessFirefox |
Only supported on systems that use an X11 server. This browser needs Xvfb and firefox to be installed on the target system. Xvfb (X Virutal Frame Buffer) is an X11 server that doesn't need any real graphics capability to run, and will happily run on servers that do not have a graphics card. This browser will first launch an instance of Xvfb, and then launch a firefox instance instructing it to use Xvfb as its display. Xvfb is expcted to be in your PATH. The path to the firefox binary can be configured in the same way as for the "firefox" browser. |
headlessChrome | Same as headlessFirefox but starts Google Chrome instead. |
JavaScript, DOM and jQuery documentation
STJS provides bridges for the basic JavaScript objects, the DOM objects, jQuery (1.6) and jQuery UI (1.8). The bridges follow as closely as possible their JavaScript counterpart. So for the moment, please use the documentation provided by other reference websites:
For JavaScript and DOM the global functions and objects (like setInterval or window) are found in the org.stjs.javascript.Global class.For jQuery, the $ object and function are found in the org.stjs.javascript.jquery.GlobalJQuery class.