30 June 2013

Automated ExtJS application testing with nodejs and jsdom during maven build.

Hi there! I believe the post will be helpful for developers who want to test a complex ExtJS application (source code available). I work on a project where the front-end part is a complex ExtJS application. It is a very rich web application which generates a lot of grids, forms and dialogs automatically. Custom managers and utility classes work on top of ExtJS framework. We use ExtJS models which are generated from Java classes (back-end is a java application) automatically. So, the problem was to write tests for this complex ExtJS application. There are very few approaches I’ve found in Internet about testing ExtJS applications. Basically, only two: 

  • Siesta is a commercial product and the license costs $500 (the “Lite” version is free, but it allows only to run “tests in-browser”, no command-line support).
  • Actually, Jasmine is OK, but as any Javascript testing frameworks, it supports a limited version of ExtJS: ext-debug.js. It means that you can’t fully test all your application code, especially if you have a really complex application which overrides some of ExtJS base classes. 

Long story short: this post is about testing automatically (during maven build) your ExtJS application using ext-all-common.js (full* uncut version of ExtJS library, which you use in your real application). The idea came to me after investigating NodeJS project. So, the idea is pretty simple: we can start NodeJS from command line during maven build. We can emulate browser with jsdom project, we can initialize ExtJS library on emulated browser the same way we initialize it in real browser when we start our real application. The benefit of this approach is that you have fully functional ExtJS library and the whole power of NodeJS (of course, including assertions - you can test your application using default nodejs “assert” module). Here is the code. (I can’t show you all my code as the project I work on is commercial). Let’s start from maven configuration: 1) You should have a nodejs executable file, so please ensure that you’ve included next plugin to your pom.xml file:
<!-- Extract NodeJS executable in TARGET -->
<plugin>
    <groupId>com.github.skwakman.nodejs-maven-plugin</groupId>
    <artifactId>nodejs-maven-plugin</artifactId>
    <version>1.0.3</version>
    <executions>
        <execution>
            <phase>verify</phase> <!-- or whichever phase you need -->
            <goals>
                <goal>extract</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- target directory for node binaries -->
        <targetDirectory>
            ${basedir}/target/nodejs/
        </targetDirectory>
    </configuration>
</plugin>
2) As nodejs binaries are platform dependent, you should add several profiles:
<profiles>
    <profile>
        <id>nodejs-windows</id>
        <activation>
            <os>
                <family>windows</family>
            </os>
        </activation>
        <properties>
            <node.executable>node.exe</node.executable>
        </properties>
    </profile>
    <profile>
        <id>nodejs-mac</id>
        <activation>
            <os>
                <family>mac</family>
            </os>
        </activation>
        <properties>
            <node.executable>node</node.executable>
        </properties>
    </profile>
    <profile>
        <id>nodejs-unix</id>
        <activation>
            <os>
                <family>unix</family>
            </os>
        </activation>
        <properties>
            <node.executable>node</node.executable>
        </properties>
    </profile>
</profiles>
3) And after that you can run your nodejs script:
<!-- Run NodeJS Script as a part of verification process -->
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.2.1</version>
    <executions>
        <execution>
            <phase>verify</phase> <!-- or whichever phase you need -->
            <goals>
                <goal>exec</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <executable>${basedir}/target/nodejs/${node.executable}</executable> <!-- depends on platform -->
        <workingDirectory>./</workingDirectory>
        <arguments>
            <argument>${basedir}/src/test/nodejs/ApplicationTest.js</argument>
        </arguments>
    </configuration>
</plugin>
The whole architecture looks the next way:
You can download a sample project to take a look at code and find out how it works. Please, find the ZIP archive here. To run the code, please unzip the archive, go to root folder and type in console (don't forget to install nodejs before running the sample code as is doesn't contain maven build):
> node ./nodejs/ApplicationTest.js
You should see something like this. * There is only 1 dirty hack which I had to make in ext-all-common.js file :-( It is because of jsdom: html element doesn't support getBoundingClientRect() method. So, I had to add try-catch block (see in code). If you will find a better way how to fix it, please let me know. For now it looks the next way:
/**
 * asolodovnicov >> fix getBoundingClientRect for element
 */
try {
    box = el.getBoundingClientRect();
} catch(e) {
    box = { top : el.offsetTop, left : el.offsetLeft }
};