Showing posts with label grunt. Show all posts
Showing posts with label grunt. Show all posts

Friday, March 1, 2013

Using StealJS in Jasmine Tests with Grunt

Grunt Jasmine Configuration

In this post, I'll detail how to use StealJS to author Javascript unit tests in Jasmine, and run them from the command line using the headless grunt-contrib-jasmine runner.

If you look in the documentation for grunt-contrib-jasmine, you'll see a section on using a custom template to run tests which use RequireJS as a dependency manager. You'll end up doing the same bit for StealJS, but will use a different template plugin: grunt-template-jasmine-steal.

First, install the necessary plugins into your local grunt project workspace:

npm install grunt-contrib-jasmine --save-dev
npm install grunt-template-jasmine-steal --save-dev

Next, import the tasks into your Gruntfile.js:

grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.registerTask('test', ['connect:test', 'jasmine']);

Finally, define the connect and jasmine plugin configs. Note that I'm setting up the connect plugin to start a local web server on port 8000, for phantomjs to connect to when running the jasmine suite:

var jsFiles = [
    'src/**/*.js',
    '!src/can/**/*.js',
    '!src/steal/**/*.js',
    '!src/thirdparty/**/*.js'
];

connect: {
    test : {
        port : 8000
    }
},

jasmine: {
    tests: {
        src: jsFiles,
        options: {
            specs: 'src/**/*_spec.js',
            host: 'http://127.0.0.1:8000/',
            template: require('grunt-template-jasmine-steal'),
            templateOptions: {
                stealOptions: {
                    stealUrl: '/src/steal/steal.js',
                    baseUrl: ''
                }
            }
        }
    }
}

Take note of the jsFiles variable I defined; that concisely lists all of my javascript source files, excluding libraries.

Cleaning and Linting your Sources

Another important build step is to clean and lint (error check and validate) your code, and thankfully grunt makes this easy on us. I use the jsbeautifier and grunt-contrib-jshint plugins for this:

grunt.loadNpmTasks('grunt-jsbeautifier');
grunt.loadNpmTasks('grunt-contrib-jshint');

        jsbeautifier : {
          files : jsFiles
        },

        jshint: {
            files: jsFiles,
            options: {
                jshintrc: '.jshintrc'
            },
        },

Note: look at the documentation for jsbeautifier to see the available configuration options (the defaults work fine for me out of the box).

Putting it All Together

Here is a complete Gruntfile.js and .jshintrc, detailing what I went through in detail above. I've set it so that the default grunt task performs the code formatting, checks using jshint, and then runs the full jasmine test suite.




Thursday, February 28, 2013

StealJS Builds with Grunt

GRUNT!

I have seen the future of javascript builds, and it is Grunt.  Please, please, please disregard my earlier post on building with maven; it just doesn't make sense to use such a heavyweight Java-based tool for building javascript projects.

Grunt is rightfully taking the Javascript world by storm. It's got a great community around it, as shown by the wealth of plugins available. It runs on Node, allows for package management via npm, and lets you start working with Javascript in a way which makes us stodgy old enterprise Java guys breathe slightly easier ;-) (more posts on that soon!).

Steal Builds, with Grunt

So naturally I'd like to share how I've gotten steal builds to work with my gruntfile. There is a grunt-steal plugin, but it hasn't yet been updated for grunt 0.4, and steal builds don't run on node anyways. (but, excitingly enough, I've heard rumors that steal will run in node sooner rather than later!)

Here are the npm packages I'm using for my steal build:

    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-exists');

My approach is to copy all of the source files into a target/ folder (ala maven!) so I can apply the package version where appropriate, and build the production.{css | js} files outside of the src dir.

Here's where I copy the source files. Note that I'm only including HTML in the phase where I am replacing the project.version and steal environment variables. As with maven, if you filter binary files it will corrupt them.

        copy: {
            build: {
                files: {
                    'target/': ['src/**/*.html']
                },
                options: {
                    processContent: function (content) {
                        return content.replace(/env\s*: 'development', \/\/ live/g, "env: 'production',").replace(/\$\{project\.version\}/g, grunt.template.process('<%= pkg.version %>'));
                    }
                }
            },
            build_other: {
                files: {
                    'target/': ['src/**/*', 'src/**/.*', '!src/**/*.html']
                }
            }
        },

Here's my corresponding HTML snippets which get replaced:

<head>
  <link href="app/production-${project.version}.css" rel="stylesheet">
</head>
<body>
   <script>
      (function() { 
        steal = { 
          env: 'development', // live 
          startFile: 'app', 
          executed: ['app/production.css'], 
          production: 'app/production-${project.version}.js' 
        }; 
      }());
    </script>
    <script src="steal/steal.js"></script>
</body>

Note also in the build_other phase, that I'm explicitly including src/**/* and src/**/.*, so that my .htaccess file comes along for the ride.

Next, I'm using the shell plugin to run the steal build, the same way as you would manually from the command line. First, I need to make the js script executable, then I run it using the target dir as the base working dir.

        shell: {
          chmodjs: {
            command: 'chmod 755 target/src/js'
          },

          steal_app: {
            command: './js app/scripts/build.js',
            options: {
                stdout: true,
                stderr: true,
                failOnError: true,
                execOptions: {
                    cwd: 'target/src'
                }
            }
          }

And voila! You now have a target/src dir with a deployable web application. My last step is to verify that our production files were built successfully.


        exists: {
            prod_files: [ 
              'target/src/app/production.js', 
              'target/src/app/production.css', 
            ]
         }

Also note that I haven't physically changed the name of the production.js file to production-{project.version}.js. I'm using a neat .htaccess trick from html5boilerplate to do my filename fingerprinting in apache. This works because I've set no-cache for *.html.

<IfModule mod_rewrite.c>
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule ^(.+)\-\d+\.\d+\.\d+\.(js|css|png|jpg|gif)$ $1.$2 [L]
</IfModule>

I'm going to go into more detail on integrating steal with jasmine tests, and exploring other grunt goodies, in (near!) future posts.