Utilizing REST with qx.Website

If you take a look at web APIs and the web service landscape nowadays you’ll notice that the predominant architectural style is REST (Representational State Transfer).

qooxdoo helps you utilize REST with our REST layer (qx.io.rest.Resource), which can be used to work against RESTful web-services in an elegant way. That means rather than requesting URLs with a specific HTTP method manually, you can define resource objects and invoke actions upon them. Our REST manual page explains this in greater detail.

Until now the REST layer was not available within qx.Website but this has changed. In order to provide the same functionality that qx.io.rest.Resource offers we had to introduce another Resource class: qx.bom.rest.Resource. This enabled us to change the API slightly where needed (i.e. smaller dependency footprint) while staying backwards-compatible (unchanged API for qx.io.rest.Resource).

So here is a small example of the usage of qx.bom.rest.Resource:

q.ready(function() {
  var resourceDescription = {
    "get": { method: "GET", url: "http://example.org/resources/{name}" },
  };
  var resource = q.rest.resource(resourceDescription);
  resource.on("getSuccess", function(e) {
    console.log(e.response);
  });
  resource.on("getError", function(e) {
    console.log(e.response);
  });
  resource.get({"name": "sample"});
});

This functionality is now available in the devel version of q (i.e. qx.Website) which is available in the download section (look for q-devel.min.js). If you want to dive deeper check out the qx.Website API documentation also.

The rest of this article simply goes into detail how qx.bom.rest.Resource differs from qx.io.rest.Resource – which is mainly interesting for those who already used the old one and want to try the new one within qx.Website:

  • The event object available in the listeners (e.g. success(), getSuccess() and getError()) is a native JavaScript object instead of a qooxdoo object (qx.event.type.Rest).
    • qx.io.rest.Resource vs. qx.bom.rest.Resource
      event.getId() => event.id
      event.getRequest() => event.request
      event.getAction() => event.action
      event.getData() => event.response
      event.getPhase() => --- (see below)
  • Methods which allow manipulation of the request (e.g. configureRequest()) will operate on an instance of qx.bom.request.SimpleXhr instead of qx.io.request.Xhr (the API is similar but not identical)
  • poll() returns no qx.event.Timer object. There are two new methods (stopPollByAction() and restartPollByAction()) available at qx.bom.rest.Resource which replace the functionality provided by the Timer object.
  • The phase support, which is a more elaborate version of readyState, is not available. So use readyState instead.
    • Phases (available only in qx.io.rest.Resource):
      • unsent, opened, sent, loading, load, success
      • abort, timeout, statusError
    • readyState (available in qx.bom.rest.Resource and qx.io.rest.Resource):
      • UNSENT
      • OPENED
      • HEADERS_RECEIVED
      • LOADING
      • DONE

We are looking forward to your feedback.

qx.Desktop on mobile devices

Some time ago, we started to think about the usage of qx.Desktop UI elements on mobile devices, especially on tablets. There should be no reason why an application using qx.Desktop couldn’t work on tablets as well. And basically that’s true, even for older qooxdoo releases. But our goal wasn’t just to make it usable, but to make it fun to use. With the recently announced diet of the widget set, we took a step into the right direction. This switch reduced the memory footprint and along with that, improved the performance on mobile devices. With this blog post, we want to introduce another step.

Mouse Emulation
Relying on mouse events, as the whole UI layer of qx.Desktop does, comes with some challenges on mobile devices. The common, webkit-based browsers on mobile devices delay the mouse events for a certain timespan, which leads to a sluggish user experience. So we decided not to listen to the native mouse events, but to create our own mouse events based on the touch events offered by the browser. For more details, check out the manual page, which covers what’s supported and how to use it in your own application. Basically, with a certain config setting your app should now also work on touch devices like a tablet (while it still might not be optimized for such a use case, of course).

If you have a touch device like a tablet at hand, just check out the most recent Widget Browser, which has this new feature turned on. Let us know how well you can interact with the app just using your fingers, and please file reports if you encounter any issues.

New Lint Module

The generator has gotten a new lint module. Recap: “Lint checking” means analysing your JavaScript code and pointing out potential pitfalls and bad practices. Those things can lead to unwanted and sometimes surprising effects although they are all syntactically correct. That’s what lint is for. In the context of qooxdoo’s tool chain you invoke the lint checking by running generate.py lint (on a library) or tool/bin/ecmalint.py (on a single file).

For a long time now we have been wanting a more unified warning system. Building your application, e.g. with generate.py build, would issue warnings about your source code, and generate.py lint would issue warnings about it, and sometimes they would find the same issue in the same places, and sometimes not, so you’d get different sets of warnings running one or the other. And if they concerned the same issue at the same code location, most likely they would report the same thing with different messages.

And if you wanted to tell the tool chain that you checked this particular piece of code and it’s ok as it is, you would need to apply different techniques to silence the warnings for lint or for the build jobs (e.g. concerning unknown globals). Or you had to disable the warning for all classes handled in this job (when you only wanted to ignore the issue for one particular file). Or you disabled the warning and it would pop up again when using the same classes in another job (e.g. when the classes belonged to a library). Or you just couldn’t.

The first step

The new lint module is now the first step to amend this. Both the lint and the build jobs will eventually use it, probably with different check configurations (as lint is supposed to be pickier as one of the compile jobs), and independent of the frontend you are using (generator job or tool/bin executable). The result will be consistent messages about code issues, a single way to configure the general depth of the checking, and a single way to treat specific issues in the code.

Here is an outline of this last point. All tailoring of individual warnings will be done in JSDoc comments in the code. This is what you know if you used the lint job before, and inserted e.g. a @lint ignoreDeprecated(alert) in your code, in order to tell lint you know about this deprecated global, but it’s in for a reason and you don’t need to be warned about it further. Two things are new:

  • For the issues that concerns it, the compiler will react on exactly the same @ hints as lint.
  • The JSDoc hints are scoped, and lookup is along the scope chain.

The last point means you can add an @lint ignoreDeprecated(alert) to the JSDoc comment above the function using the alert, or at the function enclosing this function, or at the function enclosing the function enclosing this function, …  and so on up to the top of the file, depending on which scope you want to be covered by the ignore directive.

Changes on the horizon

There are a couple of things that are already in effect or are coming around next:

  • To achieve a consistent set of @ hints that works well for both the lint and the compile subsystems, some of the existing hints will change syntax. As a prominent example, @lint ignoreUndefined(foo) for unknown globals will be removed in favor of the more general @ignore(foo). The combined effect of the new @ directive is that lint will not warn about foo, nor will the compiler, but also will the compiler not include this symbol in the build, or try to follow its dependencies (and the last two will also apply if it is a known symbol that you wish to ignore).
  • JSDoc comment parsing will be more strict than in the past. It might come up with a few deviations that have entered your code, which were missed previously.
  • There is a new manual page that shall be the authoritative source about JSDoc syntax, including all possible @ hints that you can deploy.
  • The old compiler hints #require, #use, #ignore  and so forth, will eventually be replaced by corresponding (not necessarily identical) @ hints. I.e. for class files that use it, they will look like this at the top
    /**
     * @require(foo.Bar)
     * @use(baz.Bong)
     * @ignore(yup.Fey)
     */

    instead of the old

    /*
     #require(foo.Bar)
     #use(baz.Bong)
     #ignore(yup.Fey)
    */

    So there will be single syntactic system of hints for you to use in your code, used by various subsystems of the generator.

That is a rough first overview. The implementation so far involved a rewrite of the lint code,  new code for the scope analysis, modifications to the JS syntax trees, some rewrite of the variant optimizer, and an entirely new parsing of JSDoc comments. We’ll follow up with more details on the entire topic as they arrive in the master branch.

 

Tutorial: Automated UI Testing with the qooxdoo Simulator

Having previously covered unit testing, it’s time to take a look at qooxdoo’s built-in facilities for automated UI testing. Over the course of this tutorial, we’ll set up the required infrastructure and develop a test case that interacts with the Twitter application from the previous tutorials. As always, the finished code is available on GitHub.

Click to enlarge

Simulator: Selenium support for qooxdoo

The Simulator component provides the infrastructure necessary to write GUI tests for qooxdoo applications and execute them in a real web browser by way of a Selenium server.
The Simulator is based on those parts of the Selenium project that were formerly known as “Selenium RC” and are now referred to as “Selenium 1″. While this tutorial doesn’t require in-depth Selenium knowledge, you should at least familiarize yourself with its basic concepts and capabilities before reading on.

The testing API: QxSelenium

Simulator Test cases are defined as qooxdoo classes inheriting from simulator.unit.TestCase. Similar to unit tests, they live in the namespace of the application they’re testing and support the setUp/testSomething/tearDown pattern. Test methods interact with an application by using the QxSelenium API. This consists of the DefaultSelenium API plus several qooxdoo-specific additions. You can get an API reference for these by running generate.py api in the qooxdoo SDK’s component/simulator directory and then opening /component/simulator/api/ in your browser.

Setting up the infrastructure

For the purposes of this tutorial, we’ll assume that you’re using a working directory named workspace which contains the Twitter tutorial application in a subdirectory named qooxdoo-tutorial. Replace these paths with your own as appropriate.

The test browser will load the application under test (AUT) over HTTP, so make sure you’re running a web server and qooxdoo-tutorial is accessible. If you don’t want to install a full-blown HTTP server like Apache, you can use Python’s built-in web server module. To do so, open a new shell in your workspace directory and run this command:

python -m SimpleHTTPServer

You should now be able to open the tutorial application by browsing to http://localhost:8000/qooxdoo-tutorial/build/index.html.

Also, a regular Java Runtime Environment (JRE) is necessary on your machine to run Selenium.

Required Libraries

The Simulator depends on these external libraries:

Starting the Selenium Server

In a real testing environment, the Selenium server will probably run on a separate machine – in fact, the same client might use different servers to run tests e.g. in Internet Explorer on Windows, Safari on OS X and Firefox on Linux. To keep this tutorial straightforward, however, we’ll run the server on the same machine as the AUT.
Wherever selenium-server.jar is located, in order to test qooxdoo-based applications it needs to use the qooxdoo user extensions for Selenium. They’re located in the Simulator component within the qooxdoo SDK, so start the server with the -userExtensions option set accordingly by running this command in a new shell window:

java -jar selenium-server-standalone-2.5.0.jar -userExtensions <QOOXDOO_PATH>/component/simulator/tool/user-extensions/user-extensions.js

The server should now be listening on the default port, 4444.

Test Configuration Settings

The Simulator needs several configuration settings in order to run:

  • The paths for the Rhino and Selenium Client Driver JARs
  • the host name and port of the Selenium server
  • the browser to be used for the test
  • and the URI for the application under test

All these settings are defined by overriding the simulation-run job in config.json (don’t forget to uncomment the “jobs” section if necessary):

"simulation-run" :
{
  "let" :
  {
    "SIMULATOR_CLASSPATH" : [
      "../selenium-java-2.5.0.jar",
      "../libs/*",
      "../js.jar"]
  },
 
  "environment" :
  {
    "simulator.selServer"   : "localhost",
    "simulator.selPort"     : 4444,
    "simulator.testBrowser" : "*firefox",
    "simulator.autHost"     : "http://localhost:8000",
    "simulator.autPath"     : "/qooxdoo-tutorial/build/index.html"
  }
}

The simulator.testBrowser key is particularly noteworthy. The value must be one of the browser launcher strings supported by Selenium. *firefox (for Firefox 3+) and *googlechrome should work fine on any platform provided you’re using Selenium 2.x as described in this tutorial. *safari usually only works on OS X. Internet Explorer requires some additional configuration but generally works fine for what it is. Whichever browser you choose, it must be installed on the machine that runs the Selenium Server.

The simulator.autHost and simulator.autPath settings are combined to form the URI of the tested application. Adjust these depending on your web server configuration. Also note that you can test either the source or build version of the application – just make sure it’s generated before launching the test suite by running generate.py build or generate.py source.

Making the jobs available

The Twitter tutorial application was created before the simulation-* generator jobs existed, so if you downloaded the tutorial code from Github, you’ll get a “No such job” error if you try to run them. To fix this, you need to add both simulation-build and simulation-run to the “export” list at the top of the application’s config.json file. This is not necessary for application skeletons created by more recent qooxdoo SDKs (1.3 and later).

Defining a test case

Now that we’ve got our infrastructure set up, we can finally start writing tests. First, create a new subfolder named simulation in qooxdoo-tutorial/source/class/twitter. This is the default location for Simulator tests. In this folder, create a new file named Settings.js. This will be our test case that is going to interact with the Twitter application’s settings dialog. For now, just add a test method stub that will cause the test to fail:

qx.Class.define("twitter.simulation.Settings", {
 
  extend : simulator.unit.TestCase,
 
  members :
  {
    testChangeLanguage : function()
    {
      this.fail("Test not implemented!");
    }
  }
});

Building and running the test suite

Time to see the Simulator in action. In the Twitter application’s directory, run generate.py simulation-build to create the test application. Note that there is no simulation-source job (yet) so you must run simulation-build every time you modify your test classes.

Once the build job is finished, run generate.py simulation-run. Assuming everything’s set up correctly, two Firefox windows should (very briefly) open up and you should see the result of the failing test right on the shell:

----------------------------------------------------------------------------
    Initializing: qooxdoo-tutorial
----------------------------------------------------------------------------
>>> Processing configuration
  - Warning: ! Shadowing job "simulation-run" with local one

----------------------------------------------------------------------------
    Executing: simulation-run
----------------------------------------------------------------------------
>>> Running Simulation...
>>> Load runtime: 87ms
>>> Loading tests...
>>> 1 tests ready
>>> Simulator run on Thu, 08 Sep 2011 14:22:29 GMT
>>> Application under test: http://localhost:8000/qooxdoo-tutorial/build/index.html
>>> Platform: Linux
>>> User agent: Mozilla/5.0 (X11; Linux i686; rv:6.0.2) Gecko/20100101 Firefox/6.0.2
>>> Running tests...
>>> Main runtime: 8887ms
>>> Finalize runtime: 0ms
>>> Assertion error! Test not implemented!: Called fail().
>>> Stack trace: 

>>> ERROR  twitter.simulation.Settings:testChangeLanguage
>>> Test not implemented!: Called fail().

>>> Test suite finished.
>>> 0 passed, 1 failed, 0 skipped.
>>> Simulator run finished in: 0 minutes 15 seconds.
>>> Done (0m17.20)

You’ll notice a warning about the “simulation-run” job being shadowed. Since we’re doing that on purpose, we can silence this warning by adding the top-level key “config-warnings” to config.json:

"config-warnings" :
{
  "job-shadowing" : ["simulation-run"]
},

Test development

Let’s replace that stub with something useful now: We want Selenium to use the Twitter application’s preferences window to change the language.
But first, we should set Selenium’s execution speed (the delay after each command is excuted) to a value that will allow us to actually see what’s going on, say one second. To do so, replace the this.fail line:

testChangeLanguage : function()
{
  this.getQxSelenium().setSpeed(1000);
}

The first real action of the test will be to click the “Preferences” button. This leads us to one of the main challenges when developing Selenium tests: How to locate the right element.

Locator strategies

Elements can be located using several different strategies, generic as well as as qooxdoo-specific ones. See the manual for an overview:

Simulator: Locating elements

In this tutorial, we’ll focus on the qxhv locator. Just like qxh, it traverses the application’s widget hierarchy, using a syntax similar to XPath to match the widgets it finds to criteria defined by the user.

Note: The Selenium IDE Firefox add-on and the qooxdoo Inspector can be very helpful tools for finding locators and debugging Selenium tests.

The qxhv locator allows us to find any widget with a given “label” property value:

qxhv=*/[@label=Preferences]

A word about locales

As you’ll be aware if you’ve completed the Translation tutorial, the Twitter application is localized and will automatically switch the display language if the locale of the browser it’s opened in matches one of the supported languages (German, English, French and Romanian). This means that depending on the locale of the browser you’re using to run the test suite, you may have to adjust the target value of the Preferences label locator step, e.g. qxhv=*/[@label=Einstellungen] for a German language browser.

Executing commands

To simulate a user clicking on the target identified by the locator, we need to combine it with the qxClick command:

// Click the Preferences button
var preferencesButtonLocator = "qxhv=*/[@label=Preferences]";
this.getQxSelenium().qxClick(preferencesButtonLocator);

This should open the Preferences window. To make sure the command worked, we can employ the isElementPresent command, then use an assertion so the test will fail if the window didn’t open:

// Check if the Preferences window opened
var settingsWindowLocator = "qxhv=[@classname=twitter.SettingsWindow]";
var settingsWindowPresent = this.getQxSelenium().isElementPresent(settingsWindowLocator);
this.assertTrue(settingsWindowPresent);

If the settings window was a qx.ui.window.Window, we could simply use the class name as the locator step. But that only works with classes from the qx.* name space. For a custom widget class like twitter.SettingsWindow, we need to search by classname, a plain JavaScript attribute supported by all qooxdoo objects. The @propertyName=value locator step covers these as well.

All right, time to execute the test again (don’t forget to run simulation-build again first). Assuming all went well and the test passed, the next step is to select one of the language options from the Preferences window. qx.ui.form.RadioButton also has a label property (inherited from qx.ui.basic.Atom), so we’ll use that:

// Click the radio button for Romanian
var romanianLabelLocator = "qxhv=[@classname=twitter.SettingsWindow]/*/[@label=Romanian]";
this.getQxSelenium().qxClick(romanianLabelLocator);

Obviously, if your browser’s locale is Romanian, this option will already be selected so you should choose a different one.

Following that, we want to close the Preferences window. The close button doesn’t have a label, but we can find it by looking for the file name of its icon:

// Click the window's close button
var windowCloseButtonLocator = "qxhv=[@classname=twitter.SettingsWindow]/qx.ui.container.Composite/[@icon=close\.gif]";
this.getQxSelenium().qxClick(windowCloseButtonLocator);

We don’t need to use the full resource ID of the icon since the [@property=value] step treats the value as a regular expression.

Again, we’ll use isElementPresent to check the result:

// Check if the window was closed
settingsWindowPresent = this.getQxSelenium().isElementPresent(settingsWindowLocator);
this.assertFalse(settingsWindowPresent);

This would be a good time to re-generate and run the test to make sure everything works as expected.

Verifying the language change

For the final step of this tutorial, we’ll check if the language change was correctly applied to the twitter application. The first approach might be to use isElementPresent to check for the Preferences button with the translated label value (e.g. “Preferinte” for Romanian). That won’t work, however, since the value of the “label” property is a  qx.locale.LocalizedString object, so the [@property=value] locator step will try to call toString on it. This will return the original, untranslated label so the check will fail. To get the visible, translated string, we need to call the LocalizedString’s translate() method. That’s where QxSelenium.getRunInContext comes in: It takes a locator and a snippet of JavaScript code which it uses as the body of a new function. This function will then be called in the context of the widget identified by the locator, i.e. “this” will reference the widget instance. The function’s return value is then serialized as JSON and returned by getRunInContext. We can use this to compare the translated label value to what we’re expecting:

// Get the translated string for the Preferences button label
var translatedLabel = this.getQxSelenium().getRunInContext(preferencesButtonLocator,
"return this.getLabel().translate().toString()");
// Check if the label was translated
this.assertEquals("Preferinte", translatedLabel);

And that’s it for this introduction to the Simulator. If you have further questions or encounter any problems getting the tutorial code to run, leave a comment or contact us on the qooxdoo-devel mailing list.