Intern: DRY Runners for multiple project in one workspace - node.js

While realizing that my project setup might be the root cause of my problem, here's what I'd like some help with (moving from another framework to Intern):
I've different (>20) projects in one dev workspace, so I've (>20) different directories with test code. I'd like a project to be testable on itself, but also would like to execute all suites together.
I am specifying to use RequireJS as an AMD loader and where my tests can be found per project (one for nodeJS, one for browsers) through a config file. PLUS I have one overall config file specifying all the files.
You can image there is quite the duplication, what would be a good approach to DRY this up?
Note: it would also be welcome to help to describe my problem better (more generic or less TLDR)!

Since Intern configuration files are just AMD modules you can use the normal dependency loading mechanism to load and combine configuration data from multiple files:
// in your “do everything” master configuration
define([
'projectA/tests/intern',
'projectB/tests/intern',
'projectC/tests/intern',
// …
], function () {
var configs = Array.prototype.slice.call(arguments, 0);
function getCombined(key) {
return Array.prototype.concat.apply([], configs.map(function (config) {
return config[key];
}));
}
return {
suites: getCombined('suites'),
functionalSuites: getCombined('functionalSuites'),
// …
};
});

Related

Is it bad practice to have a requirejs package with no main script?

Lets say i have a bunch of js view scripts defined as AMD modules in directory 'views'.
Rather than listing them all out in the requirejs config, i could just do this:
require = {
baseUrl: 'js',
packages: [
{ name:'views',location:'app/views' }
...
],
...
}
I then require them as ['views/sunset', 'views/ocean'] (or './ocean' if from another view) etc.
This saves me a whopping 20 seconds or so versus listing them all out individually in the require config, and arguably makes my define() calls more expressive (i.e it's clear which scripts are components, which are utilities etc)
Essentially i'm treating the directory as a package, but there is no main script, so require(['views']) would return a 404. Is there any reason why this approach might be considered bad practice? Are there issues with this that i'm not seeing?
In my previous job we did not have main.js file because each page had different modules so they were loaded dynamically based on the content of the page. I didn't notice any issues with that.
Your solution seems to be similar. If you do not have any errors, it's fine :)
main.js is not required but it is recommended by RequireJS from what I remember

Use different configuration options for different modules

In my require.js configuration, I'm using urlArgs: "bust=" + (new Date()).getTime(), to ensure that my module scripts are not being cached at all during development (this has the effect of adding a unique query parameter for each request, so it looks to a cache like a different resource).
However, there are several third party libraries I'm using that I'm not changing at all and would like for them to be cached to speed up loading and my overall development cycle. Is there a way to only apply the caching-busting config to certain modules, for instance based on the path?
The urlArgs option is used by the nameToUrl method of requirejs' context. This means that within a context all modules will share the option. Different contexts can have different options, but mixing contexts will be more hassle than it's worth: it won't be enough to define two sets of configuration options, but different modules would have to be required differently and modules from different context can't be listed in a single dependencies list.
Nevertheless, here is an example of how this can be accomplished (fiddle):
// default context
require.config({
urlArgs: "boost=" + (new Date()).getTime(),
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min",
}
});
// cached (non-boosted) context
var reqCached = require.config({
context: "cached",
paths: {
jquery: "//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min",
}
});
// define a module "inline", normally it'll go into <base>/main.js
define("main", ["jquery"], function($) {
$("body").append("<div>boosted jquery</div>");
reqCached(["jquery"], function($) {
$("body").append("<div>cached jquery</div>");
});
});
// bootstrap the "application" - load and execute the main module
requirejs(["main"], function(main) {
});
In developer tools you can see that this contrived example loads two versions of jquery - one cache-boosted (the dependency of the "main" module) and one plain (manually required with the reqCached context). Alas, it is impossible - or I'm unaware of a way - to mix and match the two contexts so they provide different sets of modules transparently.

How to test a Grunt task? Understanding and best practices

I'm a bit stuck with understanding how to write complicated Gruntfile.js and use it with tests. Am I using Grunt in a right way? I would like to ask community for help and contribute in an other way.
I'm writing a new task for Grunt and want to roll it out for wide audience on Github and npm. I want to make automated testing for this task (and I want to learn how to do it properly!).
I want to test different options combinations (about 15 by now). So, I should multiple times:
run cleanup
run my task with next options set
run tests and pass options object to the test
Some non-working code to look at for better understanding:
Gruntfile:
grunt.initConfig({
test_my_task: {
testBasic: {
options: {
//first set
}
},
testIgnore: {
options: {
//another set
}
},
//...
}
clean: {
tests: ['tmp'] // mmm... clean test directory
},
// mmm... unit tests.
nodeunit: {
tests: ['test/*.js'] //tests code is in 'tests/' dir
}
});
grunt.registerTask('test', ['test_my_task']);
I know how to check if tmp/ folder is in desired state when options object given.
The problem is putting things together.
I would ask just for template code as an answer, npo need to put working example.
PS: you can propose another testing tool, nodeunit is not a must.
PPS: crap, I could have written this in plain javascript by now! Maybe I'm doing wrong that I want to put Grunt into the unit tests? But I want to test how my task works in real environment with different options passed from Grunt...
You might want to have a look at the grunt-lintspaces configuration. The tests look like this, and it seems like a good way to do it. grunt-lintspaces uses nodeunit but a lot of plugins these days seem to.
If you don't want to test actual grunt output and instead functionality, you could use grunt-mocha-test - https://github.com/pghalliday/grunt-mocha-test which I am using for the grunt-available-tasks tests. I prefer the describe style of testing personally, it reads very well; the advantage of using this is that you actually test what your plugin does without including a ton of config in your Gruntfile; i.e. test code should be in the tests.
Grunt is well tested already so it doesn't make sense to test that its configuration works. Just test the functionality of your own plugin.

RequireJS Dynamic Paths Replacement

I have a requirejs module which is used as a wrapper to an API that comes from a different JS file:
apiWrapper.js
define([], function () {
return {
funcA: apiFuncA,
funcB: apiFuncB
};
});
It works fine but now I have some new use cases where I need to replace the implementation, e.g. instead of apiFuncA invoke my own function. But I don't want to touch other places in my code, where I call the functions, like apiWrapper.funcA(param).
I can do something like the following:
define([], function () {
return {
funcA: function(){
if(regularUseCase){
return apiFuncA(arguments);
} else {
return (function myFuncAImplementation(params){
//my code, instead of the external API
})(arguments);
}
},
funcB: apiFuncB
};
});
But I feel like it doesn't look nice. What's a more elegant alternative? Is there a way to replace the module (apiWrapper) dynamically? Currently it's defined in my require.config paths definition. Can this path definition be changed at runtime so that I'll use a different file as a wrapper?
Well, first of all, if you use Require.js, you probably want to build it before production. As so, it is important you don't update paths dynamically at runtime or depends on runtime variables to defines path as this will prevent you from running r.js successfully.
There's a lot of tools (requirejs plugins) out there that can help you dynamically change the path to a module or conditionnaly load a dependency.
First, you could use require.replace that allow you to change parts (or all) of a module URL depending on a check you made without breaking the build.
If you're looking for polyfilling, there's requirejs feature
And there's a lot more listed here: https://github.com/jrburke/requirejs/wiki/Plugins

JamJS - Still pulls for dependencies separately when compiled - Underscore not loaded for context?

A am relatively new to JamJS, and struggle to make it work properly.
I've got a very simple project based on Backbone and RequireJS, where I use JamJS to manage dependencies, namely: Backbone, _, Less, $, modernizr, underscore, bootstrap.
I tend to follow the method used by Backbone Boilerplate.
This is the code I use to get the Jam-compiled requireJS config file, together with my application-specific require config:
in html:
< script data-main="/assets/js/app/config" src="/assets/js/jam/compiled.min.js"> < /script>
'Compiled.min.js' is obviously the 600kb minified file generated by Jam.
The '/app/config' file is my Require.js configuration file where I'm planning to include all my project-specific code, not managed by the dependency manager.
Here's app/config.js:
require.config({
baseUrl:'/assets/js/',
deps: ['less','modernizer','bootstrap-jam','app/main'],
paths: {
'homeView': 'app/views/homeView'
// Use the underscore build of Lo-Dash to minimize incompatibilities.
,'lodash': '../jam/lodash/dist/lodash.underscore.min'
},
map: {
},
shim: {
}
});
(the files in deps are the ones I need on every page + my main.js - kind of a router.
The problem is that, in my homeView (which is initialized by main.js), I do this sort of thing:
define(['backbone'], function (Backbone) {
return Backbone.View.extend({
el:$('#homepageWrapper'),
initialize: function () {
this.$('#subTitle').text('This text is generated by homeView - the default Backbone View.');
}
})
});
As you can see I want Backbone to be available in this view. I assume that it's available through the compiled Jam code, however when I look in the Network panel in the Web Inspector, I see that this dependency is pulled in separately- this happens to any resource I try to use, not just Backbone.
I suspect that it might be something to do with the error I get as well, which I haven't been able to figure out. When using the compiled jam config, I get:
Uncaught Error: Module name "underscore" has not been loaded yet for
context: _. Use require([])
I'd really appreciate help with this
Thanks.

Resources