Get help with testing, discuss unit testing strategies etc.


Post by vanessa »

I am setting up Siesta-2.0.9-lite with an ExtJS 5 based project for UI unit test cases to test isolated instances of different view components using mock ajax. We are not interested in integration test cases. This project relies heavily on Sencha Command to generate multi-locale manifests such as app-en.json, app-fr.json from the main app.json configuration file while we have only one index.html that is reused for both production and dev modes of the UI. We don't have something like index.html for production and index-dev.html for development. We simply use sencha app watch for development mode to read index.html and source code directly from the file system. In production mode, we read files from target folder.

In app.json, we have a section that loads app.js as the following:
{
        "path": "app.js",
        "bundle": true
    }
The whole app.json will look like:
{
    /**
     * The application's namespace.
     */
    "name": "composition",

    /**
     * Comma-separated string with the paths of directories or files to search. Any classes
     * declared in these locations will be available in your class "requires" or in calls
     * to "Ext.require". The "app.dir" variable below is expanded to the path where the
     * application resides (the same folder in which this file is located).
     */
    "classpath": "${app.dir}/app",

    /**
     * The Sencha Framework for this application: "ext" or "touch".
     */
    "framework": "ext",

    /**
     * The name of the theme for this application.
     */
    "theme": "vcac-theme",

    "locales": [
        "en",
        "ja"
     ],
      
    /**
     * The list of required packages (with optional versions; default is "latest").
     *
     * For example,
     *
     *      "requires": [
     *          "sencha-charts"
     *      ]
     */
    "requires": [
        "ext-locale",
        "vcac-framework",
        "csp-integration"
    ],

    /**
     * Sass configuration properties.
     */
    "sass": {
        /**
         * The root namespace to use when mapping *.scss files to classes in the
         * sass/src and sass/var directories. For example, "composition.view.Foo" would
         * map to "sass/src/view/Foo.scss". If we changed this to "composition.view" then
         * it would map to "sass/src/Foo.scss". To style classes outside the app's
         * root namespace, change this to "". Doing so would change the mapping of
         * "composition.view.Foo" to "sass/src/composition/view/Foo.scss".
         */
        "namespace": "composition"
    },

    /**
     * The file path to this application's front HTML document. This is relative
     * to this app.json file.
     */
    "indexHtmlPath": "index.html",

    /**
     * The absolute URL to this application in development environment, i.e: the URL to run
     * this application on your web browser, e.g: "https://localhost/composition/index.html".
     *
     * This value is needed when build to resolve your application's dependencies if it
     * requires server-side resources. This setting is only used if you enable dynamic
     * resolution by setting "skip.resolve=0" (in .sencha/app/sencha.cfg) or if you
     * invoke "sencha app resolve".
     */
    "url": null,

    /**
     * List of all JavaScript assets in the right execution order.
     *
     * Each item is an object with the following format:
     *
     *      {
     *          // Path to file. If the file is local this must be a relative path from
     *          // this app.json file.
     *          //
     *          "path": "path/to/script.js",   // REQUIRED
     *
     *          // Set to true on one file to indicate that it should become the container
     *          // for the concatenated classes.
     *          //
     *          "bundle": false,    // OPTIONAL
     *
     *          // Set to true to include this file in the concatenated classes.
     *          //
     *          "includeInBundle": false,  // OPTIONAL
     *
     *          // Specify as true if this file is remote and should not be copied into the
     *          // build folder. Defaults to false for a local file which will be copied.
     *          //
     *          "remote": false,    // OPTIONAL
     *
     *          // If not specified, this file will only be loaded once, and cached inside
     *          // localStorage until this value is changed. You can specify:
     *          //
     *          //   - "delta" to enable over-the-air delta update for this file
     *          //   - "full" means full update will be made when this file changes
     *          //
     *          "update": "",        // OPTIONAL
     *
     *          // A value of true indicates that is a development mode only dependency.
     *          // These files will not be copied into the build directory or referenced
     *          // in the generate app.json manifest for the micro loader.
     *          //
     *          "bootstrap": false   // OPTIONAL
     *      }
     */
    "js": [{
        "path": "app.js",
        "bundle": true
    }],

    /**
     * List of all CSS assets in the right inclusion order.
     *
     * Each item is an object with the following format:
     *
     *      {
     *          // Path to file. If the file is local this must be a relative path from
     *          // this app.json file.
     *          //
     *          "path": "path/to/stylesheet.css",   // REQUIRED
     *
     *          // Specify as true if this file is remote and should not be copied into the
     *          // build folder. Defaults to false for a local file which will be copied.
     *          //
     *          "remote": false,    // OPTIONAL
     *
     *          // If not specified, this file will only be loaded once, and cached inside
     *          // localStorage until this value is changed. You can specify:
     *          //
     *          //   - "delta" to enable over-the-air delta update for this file
     *          //   - "full" means full update will be made when this file changes
     *          //
     *          "update": ""      // OPTIONAL
     *      }
     */
    "css": [{
        "path": "bootstrap.css",
        "bootstrap": true
    }],

    /**
     * Used to automatically generate cache.manifest (HTML 5 application cache manifest) file when you build
     */
    "appCache": {
        /**
         * List of items in the CACHE MANIFEST section
         */
        "cache": [
            "index.html"
        ],
        /**
         * List of items in the NETWORK section
         */
        "network": [
            "*"
        ],
        /**
         * List of items in the FALLBACK section
         */
        "fallback": []
    },

    /**
     * Extra resources to be copied along when build
     */
    "resources": [],

    /**
     * File / directory name matchers to ignore when copying to the builds, must be valid regular expressions
     */
    "ignore": [
        "^\\.svn$",
        "^\\.git$"
    ],

    /**
     * Directory path to store all previous production builds. Note that the content generated inside this directory
     * must be kept intact for proper generation of deltas between updates
     */
    "archivePath": "archive",

    /**
     * Uniquely generated id for this application, used as prefix for localStorage keys.
     * Normally you should never change this value.
     */
    "id": "a768d96b-c330-411a-b666-695af9c3b174"
}
app.js has
Ext.application({
    name: 'composition',

    extend: 'composition.Application',

    autoCreateViewport: 'Vcac.app.Viewport'
});
My app-test.js has
xt.application({
    name: 'composition',

    extend: 'Ext.app.Application',

    autoCreateViewport: false
});
My harness test.js has
var CompositionUnitTest = true;

var Harness = Siesta.Harness.Browser.ExtJS;

Harness.configure({
    title: 'Composition Test Suite',
    loaderPath: {
        'composition': 'app'
    },
    autoCheckGlobals: true,
    expectedGlobals: [ 'Ext', 'composition', 'COMPOSITON_BUNDLE', 'VCAC_BUNDLE' ],
    preload: [ 
       'bootstrap.js', 'app-test.js'
    ]
});

Harness.start({
    group: 'view',
    items: [{
        group: 'masterdetails',
        items: ['hongvan/tests/view/masterdetails/MasterDetails_1.js']
    }]
});
My test.html
<!DOCTYPE html>
<html>
    <head>
    	<title>Upgrade UI Unit Test Cases</title>
    	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <!--
            Ext JS library CSS, this is merely for the Siesta UI and is not used by your tests.
            Your tests use the ExtJS library, specified in the "preload" config of harness.
            DO NOT CHANGE THIS LINE, instead modify the "preload".
        -->
        <link rel="stylesheet" type="text/css" href="hongvan/ext-4.2.0-gpl/resources/css/ext-all.css">
        <link rel="stylesheet" type="text/css" href="hongvan/siesta-2.0.9-lite/resources/css/siesta-all.css">

        <!--
            Ext JS library, this is merely for the Siesta UI and is not used by your tests.
            Your tests use the ExtJS library, specified in the "preload" config of harness.
            DO NOT CHANGE THIS LINE, instead modify the "preload".
        -->
        <script type="text/javascript" src="hongvan/ext-4.2.0-gpl/ext-all-debug.js"></script>
		<!-- <script type="text/javascript" src="ext/bootstrap.js"></script> -->
		
        <script type="text/javascript" src="hongvan/siesta-2.0.9-lite/siesta-all.js"></script>

        <script type="text/javascript" src="cookie-js/cookie.js"></script>
       
        <script type="text/javascript">
            var Ext = Ext || {};
            Ext.beforeLoad = function(tags) {
                var locale = Cookie.get('VCAC_LOCALE');
                Ext.manifest = 'app' + "-" + locale;
                window.console.log(Ext.manifest);
            };
        </script>

        <script type="text/javascript" src="test.js"></script>
    </head>

    <body>
    </body>
</html>
My index.html
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">

    <title>composition</title>

    <script type="text/javascript" src="cookie-js/cookie.js"></script>
    <script type="text/javascript">
        var Ext = Ext || {};
        Ext.beforeLoad = function(tags) {
            var locale = Cookie.get('VCAC_LOCALE');
            Ext.manifest = 'app' + "-" + locale;
        };
    </script>

    <!-- The line below must be kept intact for Sencha Cmd to build your application -->
    <script id="microloader" type="text/javascript" src="bootstrap.js"></script>
</head>
<body></body>
</html>
The bootstrap.js specified in test.js will automatically uses app-en.json in order to load ExtJS 5 library, UI code and bootstrap.css. However, this will also loads the app.js which generate an Application with our custom View port. In order to test each UI component in isolation from our custom Viewport, I want app.json or bootstrap.js to load my app-test.js instead. This way, the test will be able to render target UI component in the DOM panel of the harness page, otherwise, I will just get a blank DOM panel. Is there a way to tell app.json to load app-test.js in testing mode? Or any other suggestion would be appreciated.

Post by mats »

Unfortunately, Sencha Cmd doesn't offer any solution to this. It would be perfect if it could build an 'all-js' file which doesn't launch the UI so you could test individual UI components easily. I've discussed this with the Sencha guys, I'll try to raise it again.

For now, you can maybe hack this yourself by changing your app.js to not launch the UI in your test version?

Post by vanessa »

Yeah. If there is no nice way of doing this, I guess I have to use global variable in app.js to determine whether I am in test mode or not.

Post Reply