Yeoman is awesome, . RequireJS is super cool. Both don't glue together, atleast not officially.There is no official support for RequireJS from yeoman team.
Here is guide on what i did to work around with things to get it working .
-
get the dependency
bower install requirejs --save
-
add script tag for require js in
<script src="bower_components/sass-bootstrap/js/tab.js"></script> <script src="bower_components/requirejs/require.js"></script>index.html
and it should look like this<script src="scripts/main.js"></script>
Sample main.js
with exported deps
require.config({
paths:{
'jquery':'../../bower_components/jquery/dist/jquery',
'text':'../../bower_components/text/text',
'scripts':'../scripts'
},
shim:{
'jquery':{
deps:[],
exports:'$'
}
}
});
- Set up grunt file
-
install dependency
npm install grunt-requirejs --save
-
load the task
grunt.loadNpmTasks('grunt-requirejs')
-
set up task
requirejs: { dist: { options: { baseUrl : '<%= config.app %>/scripts/', name : 'main', mainConfigFile : '<%= config.app %>/scripts/main.js', out : '.tmp/concat/scripts/main.js' } } },
-
add to build task the above configured task
grunt.registerTask('build', [ 'clean:dist', 'useminPrepare', 'concurrent:dist', 'autoprefixer', 'concat', 'requirejs:dist', ===> New 'cssmin', 'uglify', 'copy:dist', 'rev', 'usemin', 'htmlmin' ]);
Thats it , now running task grunt serve
should work like a charm .Build will create a file in /dist/scripts/****.main.js
Next task is to setup tests configuration .
Grunt task for testing from yeoman runs the specs in a different local server .Keep that in mind as app/
folder is not directly accessible. We might need to have some path setting to enable this. But lets take baby steps in here
To get it working with requirejs
-
add requirejs from bower with data-main to a runner file
<script data-main="spec/runner" src="bower_components/requirejs/require.js"></script> -
remove
mocha.run ()
from the script tag. We will call it after loading our spec file in ourrunner.js
file
Sample runner.js
might look something similar to
require.config({
paths:{
'jquery':'../bower_components/jquery/dist/jquery',
'underscore':'../bower_components/underscore/underscore',
'text':'../bower_components/text/text',
'scripts': '/app/scripts'
},
shim:{
'jquery':{
exports:'$'
},
'underscore':{
exports:'_'
}
}
});
var specs = [ // add all the spec files here
"User.spec",
];
require(specs, function(){
mocha.run();
});
-
Set up
Gruntfile
for testing , sinceapp/
folder is not directly available for testing i had to change a bit to update my testing config to thistest:{ options:{ open:true, port:9001, middleware:function (connect) { return [ connect.static('.tmp'), connect.static('test'), connect().use('/app', connect.static(config.app)), //connect.static(config.app), // either should work connect().use('/bower_components', connect.static('./bower_components')) ]; } } }
Thats it for setting up with requirejs
for testing purpose. But we are not done yet.
Since we are using requireJS
for modularize our code and trying to follow MV* pattern
in the code, this will lead to having one module having some dependencies on other modules.
For eg : we need jQuery, underscore , widgets
in our module controller
.
How to handle this . For doing this we need a dependency injection mechanism.
Enter Squire
Squire
is a dependency injector to use with Require.js
to make mocking dependency easy. How awesome !!!
About its usage, in the spec
file get Squire
, (we already have the path in configured in require.config
)
define(['Squire'], function(Squire) {
var injector = new Squire();
});
and now mock the dependency it needs.
injector.mock('my_dependency', {
awesomeFunction: {return 0;}
}
and now when unit testing the file Squire
will trick that "Hey ! u need a dependency 'X' , take this mock 'Y' instead and assume this is 'X' "
Now do
injector.require(['testingThisModule'],funciton(module){
decribe("Testing a module", function(){
beforeEach(){ ... do something ...}
afterEach(){ ... do whatever ... }
it("test function1", function(){
... module is available here....
... and dependency is mocked as well ...
})
})
});
Ideally this should get all the working. But this wasn't working for me .
Spending hours figuring it out finally got to use mocha async calls
hack to load my file and then keep referencing it .
Might not be the right way to do it. But kept me going.
For Async mocha takes an extra param done
which can be called when async call is done.
describe("Module", function(){
before(function(done){
injector.require(['scripts/Model/Module'], function(ModuleClass){
Module = ModuleClass;
done();
});
});
after(function(){
injector.remove();
});
it("gets me a random number" , function(){
.... refer using Module here ....
});
Though Squire.js
is great in itself. But its true capability is in doing dependency injection.
For mocking , there is also another good library called Sinon
There are a lot of useful calls that sinon
provides you with for example faketimers
, fakeXHR
, fakeServer
which I found quite useful.
Hope it helps
Phew
Extra links
Seeting up requireJS with Yeoman
Mocking AMD Modules with Squire
Dependency injection with Squire and Sinon
What is test