Sencha Touch and node.js: Part 1 – Touch, Jasmine and Grunt together

Sencha Touch and node.js: Part 1 – Touch, Jasmine and Grunt together

This post is first from the series of posts about building mobile application using Sencha Touch and node.js on the backend. We will cover how to prepare right environment, write tests, modules, split logic from controllers and views and in the end build and publish our application.

1. Requirements

Before we start you need to have few things installed or downloaded on your local machine:

2. Generate app

In your console go to the place where unpacked Sencha Touch is located. From this directory run following command:

sencha generate app TutsApp <<path/to/your/app>>

After that go where you app is stored and start server with this:

sencha fs web -p 8080 start

Open browser and type http://localhost:8080. If you see something like below:

1

your app was successfully generated.

3. Setup jasmine

Firstly, you need to create some folders and files in your app directory:

  • \app-test
  • \app-test\lib
  • \app-test\specs\helpers
  • \app-test\specs\helpers\specHelper.js
  • \app-test\specs\testHelper.js
  • \app-test.js
  • \run-test.html

Next copy downloaded jasmine sources (catalog \lib\jasmine-1.3.1) to \app-test\lib.
Open \app-test.js and put following code in it

Ext.Loader.setConfig({
    enabled: true,
    disableCaching: false // disable file caching
});

Ext.Loader.setPath({
    'TutsApp': 'app' // put here your classes location
});

Ext.require('Ext.app.Application');

var Application = null;

Ext.onReady(function() {
    Application = Ext.create('Ext.app.Application', {
        name: 'TutsApp',
        controllers: [
           // here you should put all tested controllers
        ],
        launch: function() {
            //initialize jasmine lib
            jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
            jasmine.getEnv().execute();
        }
    });
});

Above configuration will load all dependencies before jasmine run proper tests.
Now it’s time to fill run-test.html file. Open it and past this:

<html>
<head>
    <title id="page-title">Tester</title>
    <link rel="stylesheet" type="text/css" href="app-test/lib/jasmine-1.3.1/jasmine.css">
    <script type="text/javascript" src="app-test/lib/jasmine-1.3.1/jasmine.js"></script>
    <script type="text/javascript" src="app-test/lib/jasmine-1.3.1/jasmine-html.js"></script>
    <script type="text/javascript" src="touch/sencha-touch-all-debug.js"></script>
    <!-- include specs here -->
    <script type="text/javascript" src="app-test/specs/helpers/specHelper.js"></script>
    <script type="text/javascript" src="app-test/specs/testSpec.js"></script>
    <!-- test launcher -->
    <script type="text/javascript" src="app-test.js"></script>
</head>
<body>
</body>
</html>

As you see it’s only few lines of html. Firstly, we load jasmine and Sencha Touch libraries. After that we add specHelper file, in which we will put code to prevent some unexpected behaviors between tests. And in the end we append scripts with test specification and with application bootstrap.
Two files left. As first open specHelper.js and past

Ext.require('Ext.data.Model');

afterEach(function () {
    Ext.data.Model.cache = {};
});

This will clear cache of initialized model after each test.
To check that everything is working, we need to fill our test specification. In \app-test\specs\testSpec.js put following code:

describe("Basic Assumptions", function() {

    it("has ExtJS4 loaded", function() {
        expect(Ext).toBeDefined();// we check if Ext JS is loaded
        expect(Ext.getVersion().major).toEqual(2); // and if version is equal to currently avaiable
    });

    it("has loaded TutsApp code",function(){
        // In the end we check if our application was loaded too
        expect(TutsApp).toBeDefined();
    });
});

OK, now we are ready to play. Open http://localhost:8080/run-test.html (rerun server if you stop it in the meantime) and check the result of our tests. Do you see green boxes? Good job soldier! No? Check everything and try again.

4. Setup Grunt

Grunt is a cool tool which helps us automate many of development processes. It allow us to check syntax, run test and build app in one cycle. Theoretically we have possibility to do the same with Sencha CMD and Phantom but we need to install and configure everything step by step, manually. With Grunt we only need to download few package via npm and of course do some configuration too. But for sure it will be less time consuming and a lot easier than setting Phantom. Ok, in your project root directory start with this command:

npm init

You will be asked about few things, for now you can skip answering by pressing enter after each question.
Now install watch and Grunt with plugin for Jasmine:

npm install watch -g

Watch is a smart tool for watching files and rerun Grunt task when any change occurred.

npm install grunt grunt-contrib-jshint grunt-jasmine-runner grunt-contrib-watch --save-dev

flag –save-dev automatically add this dependency to your package.json file (which was generated after init).
Last thing, put in the \node_modules extracted files from grunt-sencha-plugin.zip. This plugin will help us to run both, Sencha Touch and Jasmine, with Grunt. Mentioned package is also available from npm, but to run it with Touch I was forced to made few modification.
So, now it’s time to add some configuration. In root project directory create file called Gruntfile.js and fill it with this code:

/*global module, global*/
module.exports = function (grunt) {

    grunt.initConfig({
        /**
         * Validate the source code files to ensure they
         * follow our coding convention and
         * dont contain any common errors.
         */
        jshint: {
            all: [
                "Gruntfile.js",
                "app.js",
                "app/**/*.js",
                "app-test/specs/*.js",
                "!touch/**/*.js"
            ],
            options: {
                trailing: true,
                eqeqeq: true
            }
        },
        /**
         * Setups Jasmine and runs them using PhantomJS headlessly.
         */
        sencha_touch_jasmine: {
            options: {
                specs: ["app-test/specs/**/*.js"],
                extFramework: "touch",
                extLoaderPaths   : {
                    "TutsApp" : "app"
                },
                extBootstrapFile: 'app-test.js',
                extAppName: 'TutsApp'
            },
            app: {
                extLoaderPaths   : {
                    "TutsApp" : "app"
                }
            }
        },
        /** 
        * setup watch 
        */
        watch: {
            // create new task
            frontend : {
                // point which files should be watched
                files: [
                    'app/**/*.js',
                    'app-test/**/*.js',
                    'app.js'
                ],
                // define what tasks should be run 
                // if watch trigger any changes
                tasks: ['jshint', 'sencha_touch_jasmine:app'],
                options: {
                    spawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-sencha-jasmine");
    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.registerTask("default", [
        "jshint"
    ]);
    // register new task for test
    grunt.registerTask("test", ["watch:frontend"]);

};

Now you are able to test your work. Try to run this two commands

grunt
grunt test

and the console should looks like:

1c

5. Prepare simple test

Currently only file which can be tested is our main view (\app\view\Main.js). For simple example it should be enough. So open \app-test\specs\testSpec.js and modify it to something similar to this:

    ........................................
    it("has loaded TutsApp code",function(){
        expect(TutsApp).toBeDefined();
    });

    // firstly load our view
    Ext.require('TutsApp.view.Main');
    // describe what you want to achieve
    it("Title bar has title: Welcome to Sencha Touch 2", function () {
        // initialize view
        Ext.create('TutsApp.view.Main', {
            renderTo: 'sencha_jasmine_wrapper'
        });
        // make a query to check if view was successfully rendered
        var mViw = Ext.ComponentQuery.query('main')[0],
            titleBars = mViw.query('titlebar');
        // and in the end tell jasmine what conditions
        // should be met to pass the test
        expect(titleBars[0].getTitle()).toEqual("Welcome to Sencha Touch 2");
    });
    .......................................

Run test command again. All test should be passed without errors.
This is what you should see in the console:

1b

6. Conclusion

In this post we learned how to set up and configure Sencha Touch and rest of needed tools for developing on frontend. With Grunt, we automatize hinting and testing process to make them easier, faster and more comfortable. This should help us keep quality of the source code on right level from the beginning of the project.

In next post from series, I will show you how to deal with Sencha Touch profiles and different views based on orientation, how to build controller and model and how to test them. So if you are interested, please subscribe our blog on G+.

Comments:
chandran
Reply
03/03/2015 at 9:25 AM

Hai,

Nice Article. i am new to sencha touch and grunt. i tried the above example, but when i run “grunt test” it says Task “sencha_touch_jasmine:app” not found. please help

Thanks & Regards,
chandran

Leave a Reply

Please fill all required fields


Drag circle to the rectangle on the right!

Send