﻿Matt Perdeck - JavaScriptFaster, more reliable web siteshttp://mattperdeck.com/
http://www.rssboard.org/rss-specificationBlogEngine.NET 2.5.0.6en-UShttp://mattperdeck.com/opml.axdhttp://www.dotnetblogengine.net/syndication.axdMy nameMatt Perdeck0.0000000.000000Improving JavaScript code quality with JSLint integrated with Visual Studio
<p>
<a href="http://www.jslint.com/lint.html">JSLint</a>
is a tool that goes
through your JavaScript code and points out bad coding practises, including undeclared variables.
You can now easily integrate JSLint with Visual Studio.
</p>
<h2>Contents</h2>
<ul>
<li><a href="#weaknesses">JavaScript Weaknesses</a></li>
<li><a href="#jslint">JSLint</a></li>
<li><a href="#jslint4vs2010">JSLint for Visual Studio 2010</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
<p><a name="weaknesses"></a></p>
<h2>JavaScript Weaknesses</h2>
<p>
An increasing number of web sites are using
JavaScript not only for non-critical features such as client side validation, but also for mission critical core features.
This to the point where serious application frameworks such as
<a href="http://knockoutjs.com/">Knockout</a> and
<a href="http://backbonejs.org/">Backbone</a> are now in widespread use.
</p>
<p>
The problem is that JavaScript was <a href="http://en.wikipedia.org/wiki/JavaScript#Birth_at_Netscape">never meant for large scale programming</a>:
</p>
<ol>
<li>
Using an undeclared variable doesn't result in an exception, but in a new global variable - making JavaScript very error prone.
</li>
<li>
No compiler, so no compile time checking for syntax errors, etc.
</li>
<li>
No support for strong typing - making it harder for developers to write correct code and for browser vendors to write
<a href="http://blogs.msdn.com/b/ie/archive/2012/06/13/advances-in-javascript-performance-in-ie10-and-windows-8.aspx">efficient interpreters/run time compilers</a>.
</li>
<li>
No classes, no generics - classes can be simulated, but in roundabout ways.
</li>
<li>
JavaScript programs travel over the wire as source code, rather than more efficient
<a href="http://en.wikipedia.org/wiki/Bytecode">bytecode</a>.
</li>
</ol>
<p>
With the
<a href="http://www.technologyreview.com/view/426083/html5-triumphant-silverlight-flash-discontinuing/">demise of Silverlight</a>
however, there are now no alternatives to JavaScript that solve these problems.
</p>
<p><a name="jslint"></a></p>
<h2>JSLint</h2>
<p>
<a href="http://www.jslint.com/lint.html">JSLint</a>
takes a little bit of the sting out of JavaScript programming.
It
is essentially a command line tool that goes through your program and picks up likely bugs and bad coding practices.
Those bad practices have been set out by its author, Douglas Crockford,
<a href="http://javascript.crockford.com/code.html">on his site</a>.
</p>
<p>
JSLint has 30 options determining what likely bugs or practices to report on, from white space styling issues to
disallowing <i>eval</i> and ++.
Those options can go on the command line or in your source code as a specially formatted comment line.
The naming of some options such as
<i>Tolerate stupidity</i>
and
<i>Tolerate messy white space</i>
probably give some insight into Mr. Crockford's personality.
</p>
<p>
JSLint will pick up undeclared variables, but not type violations.
</p>
<h3>Other linting programs</h3>
<p>
JSLint is not the only linting program available. Others include:
</p>
<ul>
<li>
<a href="http://www.toptensoftware.com/minime/lint">MiniMe</a>
</li>
<li>
<a href="http://www.javascriptlint.com/">JavaScript Lint</a>
</li>
</ul>
<p><a name="jslint4vs2010"></a></p>
<h2>Lint in Visual Studio 2010</h2>
<p>
You could do you linting from the command line, but having it integrated with Visual Studio is much better.
A number of solutions set out to do just that, including:
</p>
<ul>
<li><a href="http://www.codeproject.com/Articles/21438/JSLint-VS-JavaScript-Verifier-for-Visual-Studio">JSLint.VS</a> - fairly old, seems to be not supported anymore.</li>
<li><a href="http://jason.diamond.name/weblog/2008/08/09/verifying-javascript-with-jslint-and-visual-studio/#content">Simple solution by Jason Diamond</a> - maybe a bit hacky.</li>
<li><a href="http://www.javascriptlint.com/docs/running_from_your_ide.htm">JavaScript Lint integration with Visual Studio</a> - very old.</li>
<li><a href="http://the-taylors.org/blog/2010/07/09/add-jslint-checking-to-visual-studio/">Instructions by Dave Taylor</a> on how to add JSLint to Visual Studio as an external command.</li>
<li><a href="http://blog.outsharked.com/2011/08/sharplinter-command-line-tool-for.html">sharpLinter</a> - can be used from command line, built in minifier.</li>
<li><a href="http://jslint4vs2010.codeplex.com/">JSLint for Visual Studio 2010</a> - probably the most popular solution.</li>
</ul>
<p>
Of these options, JSLint for Visual Studio 2010 is the only true Visual Studio plugin. It has some nice features, including:
</p>
<ul>
<li>
Very easy to install - download the .vsix and double click it.
</li>
<li>
Easy to configure from the Tools menu.
</li>
<li>
Options to lint CSS, HTML and JavaScript.
</li>
<li>
Option to validate each time you do a build, and to break the build if JSLint complains. Sadly, this does not break TFS Builds.
</li>
<li>
Import / export settings to an .xml file, for backup to to share with your team.
</li>
<li>
Issues found by JSLint appear in the Error List, like C# compile errors.
</li>
<li>
Ability to skip linting for individual files and entire directories. Useful, because JSLint doesn't like minified code.
</li>
<li>
Ability to not lint certain sections of code, using special comment lines.
</li>
</ul>
<p><a name="conclusion"></a></p>
<h2>Conclusion</h2>
<p>
It makes sense to lint your JavaScript to at least catch undeclared variables.
Unlike <a href="http://aspnetperformance.com/post/Unit-testing-JavaScript-as-part-of-TFS-Build.aspx">unit testing JavaScript from within Visual Studio</a>, linting JavaScript is now very easy to achieve.
</p>
http://mattperdeck.com/post/Improving-JavaScript-code-quality-with-JSLint-integrated-with-Visual-Studio.aspx
http://mattperdeck.com/post/Improving-JavaScript-code-quality-with-JSLint-integrated-with-Visual-Studio.aspx#commenthttp://mattperdeck.com/post.aspx?id=8b98e9e4-04ef-49be-a261-bda5bd129e6aWed, 25 Jul 2012 11:29:00 -0400JavaScriptmperdeckhttp://mattperdeck.com/pingback.axdhttp://mattperdeck.com/post.aspx?id=8b98e9e4-04ef-49be-a261-bda5bd129e6a9http://mattperdeck.com/trackback.axd?id=8b98e9e4-04ef-49be-a261-bda5bd129e6ahttp://mattperdeck.com/post/Improving-JavaScript-code-quality-with-JSLint-integrated-with-Visual-Studio.aspx#commenthttp://mattperdeck.com/syndication.axd?post=8b98e9e4-04ef-49be-a261-bda5bd129e6aCache busting using assembly version for RequireJS / ASP.NET MVC projects<ul>
<li><a href="http://mattperdeck.com/myfiles/RequireJSCacheBustingThroughVersioning/RequireJSAndVersioningDownload.zip">Download sample code</a></li>
</ul>
<p>Shows how to improve your ASP.NET MVC web site's performance through <a href="http://developer.yahoo.com/performance/rules.html#expires">far future client side caching</a> of RequireJS modules, while still forcing the browser to refresh its cache the moment you introduce a new version of your modules.</p>
<h2>Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#cachebusting">Cache Busting 101</a></li>
<li><a href="#cachebustingrequirejs">Cache Busting with RequireJS</a></li>
<li><a href="#assemblyversion">Using the assembly version</a></li>
<li><a href="#htmlhelper">Putting it together with an Html Helper</a></li>
</ul>
<p><a name="introduction"></a></p>
<h2>Introduction</h2>
<p><a href="http://requirejs.org/">RequireJS</a> generates script tags to load modules. To increase web site performance, you want to configure IIS to send response headers that tell the browser to cache those modules for up to a year - the maximum according to the HTTP standard (<a href="http://developer.yahoo.com/performance/rules.html#expires">why</a>; <a href="http://www.iis.net/ConfigReference/system.webServer/staticContent/clientCache">how</a>).</p>
<p>However, when you update one or more modules, you don't want your users to keep using the old versions for up to a year. You want them to update their caches right away.</p>
<p>In this article we'll see how to do cache busting based on the version of the assembly running your web site code. The download contains a working example.</p>
<p><a name="cachebusting"></a></p>
<h2>Cache Busting 101</h2>
<p>It is very easy to get a browser to refresh its cache. Take this script tag:</p>
<pre>&lt;script src="/Scripts/main.js" type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>All you need to do is add some sort of version in a query string:</p>
<pre>&lt;script src="/Scripts/main.js?v=1.0.0.0" type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>Then when you update the version:</p>
<pre>&lt;script src="/Scripts/main.js?v=1.1.0.0" type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>The browser will look for <em>main.js?v=1.1.0.0</em> in its cache, won't find it, and request it from the server.</p>
<p>Cache busting this way has pros and cons:</p>
<ul>
<li><strong>Advantage</strong> - Very simple. Because you're using a query string, there is no need to change the name of main.js on the server.</li>
<li><strong>Disadvantage</strong> - Loss of caching performance. When you introduce the query string, <a href="https://developers.google.com/speed/docs/best-practices/caching#LeverageProxyCaching">proxies</a> and the <a href="http://learn.iis.net/page.aspx/154/walkthrough-iis-output-caching/#05">IIS kernel cache</a> no longer cache your file.</li>
</ul>
<p>The optimal cache busting method involves changing the file name itself, such as <em>main.1.1.0.0.js</em> instead of <em>main.js?v=1.1.0.0</em>. There are several packages that will do this for you on the fly (<a href="http://aspnetperformance.com/post/Package-that-speeds-up-loading-of-JavaScript-CSS-and-image-files.aspx">example</a>), but they don't integrate with RequireJS. So we'll stick with query strings in the rest of this article.</p>
<p><a name="cachebustingrequirejs"></a></p>
<h2>Cache Busting with RequireJS</h2>
<p>RequireJS lets you add a query string to all script tags it generates with the <em>urlTags</em> configuration option:</p>
<pre><span style="color: red;">&lt;script type="text/javascript"&gt; var require = { urlArgs: "v=1.0.0.0" }; &lt;/script&gt;</span>
&lt;script data-main="Scripts/main"
src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js"
type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>If you run the sample site in the download in Chrome and inspect the DOM (right click anywhere on the page | Inspect element), you'll find that RequireJS has generated this:</p>
<p><img src="/myfiles/RequireJSCacheBustingThroughVersioning/chromeInspectElement.png" alt="" /></p>
<p><a name="assemblyversion"></a></p>
<h2>Using the assembly version</h2>
<p>So far so good. However, you don't want to change your HTML manually each time you do a new release - too clumsy and error prone.</p>
<p>It's much more attractive to use the assembly version of your site:</p>
<ul>
<li>Easy to update manually - right click your web site project | Properties | Assembly Information.</li>
<li>Can be updated automatically by TFS Build (<a href="http://blogs.msdn.com/b/jimlamb/archive/2010/02/12/how-to-create-a-custom-workflow-activity-for-tfs-build-2010.aspx">how</a>, <a href="http://www.ewaldhofman.nl/post/2010/05/13/Customize-Team-Build-2010-e28093-Part-5-Increase-AssemblyVersion.aspx#id_02e7b082-ce95-49a9-92e9-7dc88887b377">how</a>, <a href="http://www.richard-banks.org/2010/07/how-to-versioning-builds-with-tfs-2010.html">how</a>).</li>
</ul>
<p>You can get the assembly version via the currently executing assembly:</p>
<pre>string version =
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
</pre>
<p>However, this won't work if your site is hosted on a shared hosting company that uses <a href="http://stackoverflow.com/questions/2617454/what-is-medium-trust-in-asp-net">Medium Trust</a>, such as GoDaddy, because the call to GetExecutingAssembly is not allowed for security reasons in such an environment.</p>
<p><a name="htmlhelper"></a></p>
<h2>Putting it together with an Html Helper</h2>
<p>Lets first create an Html Helper that returns the assembly version:</p>
<pre>namespace RequireJSAndVersioning.Helpers
{
public static class AssemblyVersionHelper
{
public static string AssemblyVersion(this HtmlHelper helper)
{
return System.Reflection.Assembly.GetExecutingAssembly()
.GetName().Version.ToString();
}
}
}
</pre>
<p>Now import your helper's namespace in your razor file:</p>
<pre>@using RequireJSAndVersioning.Helpers
</pre>
<p>Finally, use the helper with the RequireJS configuration section:</p>
<pre>&lt;script type="text/javascript"&gt;
var require = {
urlArgs: "v=<span style="color: red;">@Html.AssemblyVersion()</span>"
};
&lt;/script&gt;
&lt;script data-main="Scripts/main"
src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js"
type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>As a result of all this, whenever the assembly version of your site changes, all your visitors' browsers will request the latest versions of your modules from your server.</p>http://mattperdeck.com/post/Cache-busting-using-assembly-version-for-RequireJS-ASPNET-MVC-projects.aspx
http://mattperdeck.com/post/Cache-busting-using-assembly-version-for-RequireJS-ASPNET-MVC-projects.aspx#commenthttp://mattperdeck.com/post.aspx?id=255fb7c8-686a-4cf7-99b3-ff21c97a31faThu, 19 Jul 2012 12:19:00 -0400JavaScriptmperdeckhttp://mattperdeck.com/pingback.axdhttp://mattperdeck.com/post.aspx?id=255fb7c8-686a-4cf7-99b3-ff21c97a31fa39http://mattperdeck.com/trackback.axd?id=255fb7c8-686a-4cf7-99b3-ff21c97a31fahttp://mattperdeck.com/post/Cache-busting-using-assembly-version-for-RequireJS-ASPNET-MVC-projects.aspx#commenthttp://mattperdeck.com/syndication.axd?post=255fb7c8-686a-4cf7-99b3-ff21c97a31faHow to use the RequireJS optimizer with jQuery loaded from CDN and plugins as shims<ul>
<li><a href="http://mattperdeck.com/myfiles/RequireJSjQueryPlugins/jQueryPluginsDownload.zip">Download sample code</a></li>
</ul>
<p>Describes how to use the RequireJS optimizer with code that loads jQuery from a CDN and uses shims for jQuery plugins, avoiding the need to make the plugins into AMD modules.</p>
<h2>Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#v0">Version 0 - simple site without RequireJS</a></li>
<li><a href="#v1">Version 1 - first unsuccessful attempt</a></li>
<li><a href="#v2">Version 2 - using a new AMD module that loads the plugins</a></li>
<li><a href="#v3">Version 3 - using a second AMD module to load all plugins</a></li>
</ul>
<p><a name="introduction"></a></p>
<h2>Introduction</h2>
<p>It is now very common for web sites to <a href="https://developers.google.com/speed/libraries/devguide">load jQuery from a CDN</a> and to also use a number of jQuery plugins. Because most plugins are not available from a CDN, they need to be stored on the web server.</p>
<p>It would be great to use <a href="http://requirejs.org/">RequireJS</a> to manage the dependencies of your code to the plugins and jQuery, so the plugins and jQuery are loaded automatically when your code loads. Because jQuery plugins are not AMD (Asynchronous Module Definition) modules, they need to be configured in RequireJS as <a href="http://requirejs.org/docs/api.html#config-shim">shims</a>.</p>
<p>However, the RequireJS documentation for shims points out that you can't use the RequireJS optimizer with code that uses both shims and libraries loaded from a CDN. This turns out to be true, but only to a certain extent. This article shows how to get around this limitation by loading your AMD modules code and your (non-AMD) plugins in separate stages.</p>
<p><a name="v0"></a></p>
<h2>Version 0 - simple site without RequireJS</h2>
<p>To investigate this issue, I created a simple single page site without RequireJS at first, with a main.js file, two more custom JavaScript files and two jQuery plugins, <a href="http://coryschires.com/jquery-splatter-plugin/">Splatter</a> and <a href="http://akquinet.github.com/jquery-toastmessage-plugin/">Toastmessage</a> (version 0 in the download).</p>
<p>Here is the code:</p>
<p>html (note the long list of script tags!):</p>
<pre>&lt;h2&gt;Index Page&lt;/h2&gt;
&lt;p&gt;
&lt;button onclick="model.save_click()"&gt;Save&lt;/button&gt;
&lt;button onclick="model.splat_click()"&gt;Splat!&lt;/button&gt;
&lt;/p&gt;
&lt;div id="splatters"&gt;&lt;/div&gt;
&lt;script src="Scripts/dataAccess.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="Scripts/ticket.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="Scripts/lib/jquery.splatter.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="Scripts/lib/jquery.toastmessage.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="Scripts/main.js" type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p>main.js:</p>
<pre>function TicketsViewModel() {
this.save_click = function () {
var ticket = new Ticket("Economy", 199.95);
var message = ticket.save();
$().toastmessage('showNoticeToast', message);
}
this.splat_click = function () {
$('#splatters').splatter({
height: 250,
width: 700,
splat_count: 120
});
}
}
var model = new TicketsViewModel();
</pre>
<p>ticket.js:</p>
<pre>var Ticket = function (name, price) {
this.name = name;
this.price = price;
this.save = function () {
return DataAccess.save(this.name + ' at ' + this.price);
};
};
</pre>
<p>dataAccess.js:</p>
<pre>var DataAccess = (function () {
var my = {};
my.save = function (data) {
//TODO: Save data
return('Saving: ' + data);
};
return my;
} ());
</pre>
<p>Next step is to start using RequireJS for dependency management.</p>
<p><a name="v1"></a></p>
<h2>Version 1 - first unsuccessful attempt</h2>
<p>First lets make the two custom JavaScript files into AMD modules (<a href="http://requirejs.org/docs/api.html#define">details</a>).</p>
<p>ticket.js:</p>
<pre><span style="color: red;">define(['dataAccess'], function (DataAccess) { return (</span>function (name, price) {
this.name = name;
this.price = price;
this.save = function () {
return DataAccess.save(this.name + ' at ' + this.price);
};
}<span style="color: red;">); });</span>
</pre>
<p>dataAccess.js:</p>
<pre><span style="color: red;">define(</span>{
'save': function (data) {
//TODO: Save data
return('Saving: ' + data);
}
}<span style="color: red;">);</span>
</pre>
<p>We'll make main.js into an AMD module as well (red code). Additionally (green code), we'll introduce <a href="http://requirejs.org/docs/api.html#config-shim">shims</a> for the jQuery plugins. Finally, we'll <a href="http://requirejs.org/docs/api.html#config-paths">point RequireJS at the download path</a> for jQuery.</p>
<p>Note that the plugins and the backup file for jQuery all live in the lib subdirectory. Also, RequireJS wants you to leave off the .js extension.</p>
<pre><span style="color: green;">requirejs.config({ paths: { jquery: [ 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min', // If the CDN location fails 'lib/jquery-1.7.2.min' ] }, shim: { 'lib/jquery.splatter': ['jquery'], 'lib/jquery.toastmessage': ['jquery'] } }); // ----------------------------------------</span>
<span style="color: red;">require(['ticket', 'jquery', 'lib/jquery.splatter', 'lib/jquery.toastmessage'], function (Ticket, $) {</span>
function TicketsViewModel() {
this.save_click = function () {
var ticket = new Ticket("Economy", 199.95);
var message = ticket.save();
$().toastmessage('showNoticeToast', message);
}
this.splat_click = function () {
$('#splatters').splatter({
height: 250,
width: 700,
splat_count: 120
});
}
}
model = new TicketsViewModel();
<span style="color: red;">});</span>
</pre>
<p>Finally, we'll replace the long list of script tags with a single load of the require library from <a href="http://cdnjs.com/">CDNJS</a>:</p>
<pre>&lt;h2&gt;Index Page&lt;/h2&gt;
&lt;p&gt;
&lt;button onclick="model.save_click()"&gt;Save&lt;/button&gt;
&lt;button onclick="model.splat_click()"&gt;Splat!&lt;/button&gt;
&lt;/p&gt;
&lt;div id="splatters"&gt;&lt;/div&gt;
<span style="color: red;">&lt;script data-main="Scripts/main" src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.0.2/require.min.js" type="text/javascript"&gt;&lt;/script&gt;</span>
</pre>
<h3>Fails when optimized</h3>
<p>When you run this, you'll find that this works well. However, when you then <a href="http://requirejs.org/docs/optimization.html">optimize the code</a> and run the result, you'll find that the jQuery plugins get loaded before jQuery itself, causing a JavaScript error.</p>
<p>This is because the plugins have been combined with all other code into main.js - except for jQuery, which is loaded from the CDN. Because the plugins are not AMD modules and so have not been marked as dependent on jQuery, they get loaded before jQuery itself has been loaded.</p>
<p>There are two solutions to this:</p>
<ol>
<li>Make the plugins into AMD modules and make them dependent on jQuery. This way, their code will only be executed after jQuery has loaded. However, this had the disadvantage that you (and your team members) have to remember to convert new plugins and new versions of plugins whenever they are introduced to the site.</li>
<li>Create a new AMD module that loads the plugins. By making that module dependent on jQuery, it will only load the plugins after jQuery has loaded. This way, you don't have to convert the plugins, but there will be at least one additional download.</li>
</ol>
<p>I chose the second option because it makes maintenance a bit easier. Note that your site might be loading a lot more stuff besides JavaScript files, such as stylesheets and images. This will reduce the impact of the additional file load.</p>
<p><a name="v2"></a></p>
<h2>Version 2 - using a new AMD module that loads the plugins</h2>
<p>Lets first create the new AMD module. This has a dependency on jQuery, so won't be executed until jQuery has loaded. It then calls require with the two plugins as dependencies. That will prompt RequireJS to load the plugins.</p>
<p>jQueryPlugins.js:</p>
<pre>define(['jquery'], function ($) {
// Only once jquery has loaded, load the jQuery plugins
require(['lib/jquery.splatter', 'lib/jquery.toastmessage'], function () {
// Do nothing
});
});
</pre>
<p>Now change main.js, to replace the dependency on the two plugins with a dependency on jQueryPlugins</p>
<pre>...
require(['ticket', 'jquery', <span style="color: red;">'jQueryPlugins'</span>], function (Ticket, $) {
...
</pre>
<p>RequireJS tends to be vigorous when it comes to finding modules to combine into a single file. We need to tell RequireJS not to combine the plugins into main.js, by excluding them in the <a href="http://requirejs.org/docs/optimization.html#wholeproject">app.build.js</a> file which controls the optimization process.</p>
<p>app.build.js:</p>
<pre>({
appDir: "../",
baseUrl: "Scripts",
dir: "../../RequireJSTrial.Site-build",
modules: [
{
name: "main"<span style="color: red;">, exclude: [ 'lib/jquery.splatter', 'lib/jquery.toastmessage' ]</span>
}
],
paths: {
jquery: "empty:"
}
})
</pre>
<h3>Better, but not good enough</h3>
<p>If you now optimize the code and run it, you'll find that it now runs well (see the RequireJSTrial.Site-build directory in version 2 in the downloads).</p>
<p>However, looking at a waterfall chart of the page load, it is clear that the plugins are loaded separatedly. Which is no wonder, seeing that we went out of our way to stop them from being combined into main.js.</p>
<p><img style="width: 660px;" src="/myfiles/RequireJSjQueryPlugins/version2waterfall.png" alt="Version 2 waterfall" /></p>
<p>The solution is to another AMD module which is dependent on the jQuery plugins. By listing it as a module in app.build.js, that module and the plugins will be combined into one file. We can then get jQueryPlugins.js to load this one file, instead of the individual plugins.</p>
<p>This all leads to ...</p>
<p><a name="v3"></a></p>
<h2>Version 3 - using a second AMD module to load all plugins</h2>
<p>First we need the second module that loads all plugins.</p>
<p>jQueryPluginsCollection.js:</p>
<pre>define(['lib/jquery.splatter', 'lib/jquery.toastmessage'], function ($) {
// Do nothing
});
</pre>
<p>Update jQueryPlugins.js so it loads jQueryPluginsCollection.js instead of loading the plugins individually.</p>
<p>jQueryPlugins.js:</p>
<pre>define(['jquery'], function ($) {
// Only once jquery has loaded, load the jQuery plugins
require([<span style="color: red;">'jQueryPluginsCollection'</span>], function () {
// Do nothing
});
});
</pre>
<p>Finally, list jQueryPluginsCollection.js as a module in app.build.js. That way, the optimizer will combine and minify jQueryPluginsCollection.js and its dependencies (being the plugins). We also need to tell the optimizer not to combine it with main.js.</p>
<p>app.build.js:</p>
<pre>({
appDir: "../",
baseUrl: "Scripts",
dir: "../../RequireJSTrial.Site-build",
modules: [
{
name: "main",
// Exclude these, otherwise they get combined into main.js,
// meaning they'll be loaded before jQuery comes in from
// the CDN.
exclude: [
<span style="color: red;">'jQueryPluginsCollection',</span>
'lib/jquery.splatter',
'lib/jquery.toastmessage'
]
}<span style="color: red;">, { name: "jQueryPluginsCollection" }</span>
],
paths: {
jquery: "empty:"
}
})
</pre>
<p>If you now run the optimizer and load the page, you'll see this waterfall. This shows how all plugins are loaded in one go after jQuery has loaded:</p>
<p><img style="width: 660px;" src="/myfiles/RequireJSjQueryPlugins/version3waterfall.png" alt="Version 3 waterfall" /></p>http://mattperdeck.com/post/How-to-use-the-RequireJS-optimizer-with-jQuery-loaded-from-CDN-and-plugins-as-shims.aspx
http://mattperdeck.com/post/How-to-use-the-RequireJS-optimizer-with-jQuery-loaded-from-CDN-and-plugins-as-shims.aspx#commenthttp://mattperdeck.com/post.aspx?id=c5f0b227-d3e1-4786-a623-8cb3e37036cbTue, 17 Jul 2012 09:44:00 -0400JavaScriptmperdeckhttp://mattperdeck.com/pingback.axdhttp://mattperdeck.com/post.aspx?id=c5f0b227-d3e1-4786-a623-8cb3e37036cb4http://mattperdeck.com/trackback.axd?id=c5f0b227-d3e1-4786-a623-8cb3e37036cbhttp://mattperdeck.com/post/How-to-use-the-RequireJS-optimizer-with-jQuery-loaded-from-CDN-and-plugins-as-shims.aspx#commenthttp://mattperdeck.com/syndication.axd?post=c5f0b227-d3e1-4786-a623-8cb3e37036cbUnit testing JavaScript as part of TFS Build<ul>
<li><a href="http://mattperdeck.com/myfiles/UnitTestingJavaScript/WorkingSample.zip">Download sample code</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#sample">Working sample code</a></li>
<li><a href="#compare">Comparing QUnit versus Jasmine</a></li>
<li><a href="#vs2012">Visual Studio 2012 and Chutzpah</a></li>
<li><a href="#vs2010">Integrating Jasmine unit tests in Visual Studio 2010 and TFS 2010</a></li>
</ul>
<p><a name="introduction"></a></p>
<h2>Introduction</h2>
<p>Visual Studio makes it very easy to unit test your C# or Visual Basic code. Even better, TFS can run your unit tests as part of a build, providing a bit more confidence in the quality of your code.</p>
<p>However, these days, a large proportion of a site's functionality is often not coded in C# or Visual Basic, but in JavaScript. Unfortunately, there is no obvious support for unit testing JavaScript in Visual Studio as of July 2012.</p>
<p>This article shows how to integrate JavaScript unit testing in your ASP.NET MVC solution. The objective here is to have the JavaScript unit tests execute whenever you run your unit tests - whether from the Test View window in Visual Studio or as part of a TFS build.</p>
<p><a name="sample"></a></p>
<h2>Working sample code</h2>
<p>To provide a working example of all this, the download contains:</p>
<ul>
<li>A <em>very</em> simple ASP.NET MVC site without JavaScript testing</li>
<li>The same site, with JavaScript testing added</li>
</ul>
<p>You may have to <a href="http://blogs.msdn.com/b/brada/archive/2009/12/11/visual-studio-project-sample-loading-error-assembly-could-not-be-loaded-and-will-be-ignored-could-not-load-file-or-assembly-or-one-of-its-dependencies-operation-is-not-supported-exception-from-hresult-0x80131515.aspx">unblock the zip file</a> before unzipping it, so Visual Studio will properly run the solution that's inside - right click zip file | Properties | Unblock.</p>
<p><a name="compare"></a></p>
<h2>Comparing QUnit versus Jasmine</h2>
<p>There are many JavaScript unit testing frameworks out there (<a href="http://stackoverflow.com/questions/300855/looking-for-a-better-javascript-unit-test-tool">overview</a>). The two most popular ones at the time of writing however are QUnit and Jasmine.</p>
<table border="1" cellspacing="0" cellpadding="4">
<tbody>
<tr>
<td valign="top">&nbsp;</td>
<td valign="top"><a href="http://docs.jquery.com/QUnit">QUnit</a> 1.8.0</td>
<td valign="top"><a href="http://pivotal.github.com/jasmine/">Jasmine</a> 1.2.0</td>
</tr>
<tr>
<td valign="top">Runs in</td>
<td valign="top">web page</td>
<td valign="top">web page</td>
</tr>
<tr>
<td valign="top">Written in</td>
<td valign="top">JavaScript</td>
<td valign="top">JavaScript</td>
</tr>
<tr>
<td valign="top">Style</td>
<td valign="top">Similar to xUnit
<pre>test("increment a variable", function() {
var a = 0;
a++;
equal(a, 1, "should be 1");
});
</pre>
</td>
<td valign="top">Behavior Driven Development (<a href="http://dannorth.net/introducing-bdd/">BDD</a>)
<pre>it('should increment a variable',
function () {
var a = 0;
a++;
expect(a).toEqual(1);
});
</pre>
</td>
</tr>
<tr>
<td valign="top">Grouping of tests</td>
<td valign="top">
<pre>module("core");
test("increment a variable", function() {
...
});
test("decrement a variable", function() {
...
});
</pre>
</td>
<td valign="top">
<pre>describe('Calculator',
function () {
it('can increment',
function () {
...
});
it('can decrement',
function () {
...
});
});
</pre>
</td>
</tr>
<tr>
<td valign="top">Groups can contain groups</td>
<td valign="top">No</td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">ReSharper support</td>
<td valign="top">Yes (version 6)</td>
<td valign="top">Yes (version 7)</td>
</tr>
<tr>
<td valign="top">Supports spies, stubbing, mocking</td>
<td valign="top">No<sup>1</sup></td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">Supports async tests<sup>2</sup></td>
<td valign="top">Yes</td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">Supports testing for exceptions</td>
<td valign="top">Yes</td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">Built in assertions</td>
<td valign="top"><a href="http://docs.jquery.com/QUnit#API_documentation">5</a> unique + 4 <em>not</em> versions</td>
<td valign="top"><a href="https://github.com/pivotal/jasmine/wiki/Matchers">12</a> + separate <em>not</em> modifier</td>
</tr>
<tr>
<td valign="top">Supports custom assertions</td>
<td valign="top">No</td>
<td valign="top"><a href="https://github.com/pivotal/jasmine/wiki/Matchers">Yes</a></td>
</tr>
<tr>
<td valign="top">Setup/Teardown functions<sup>3</sup></td>
<td valign="top">Yes</td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">Can mock the JavaScript clock<sup>4</sup></td>
<td valign="top">No</td>
<td valign="top">Yes</td>
</tr>
<tr>
<td valign="top">Can check for missing vars<sup>5</sup></td>
<td valign="top">Yes</td>
<td valign="top">No</td>
</tr>
<tr>
<td valign="top">Automatically resets test DOM area after each test</td>
<td valign="top">Yes
<pre>&lt;div id="qunit-fixture"&gt;
content wiped before each test
&lt;/div&gt;
</pre>
</td>
<td valign="top">No</td>
</tr>
<tr>
<td valign="top">Optionally disables try catch<sup>6</sup></td>
<td valign="top">Yes</td>
<td valign="top">No</td>
</tr>
</tbody>
</table>
<p><sup>1</sup>A separate package, <a href="http://sinonjs.org/">SinonJs</a>, can provide support for spies and stubbing and mocking for QUnit tests <br /> <sup>2</sup>Pause the runner to wait for an async response from the server, setTimeout, etc. <br /> <sup>3</sup>Executed before/after each test. <br /> <sup>4</sup>So you don't have to wait 10 seconds for a 10 second timeout. <br /> <sup>5</sup>Missing var in variable declaration makes variable global. <br /> <sup>6</sup>Depending on your browser, can make it easier to get a stack trace.</p>
<p>Both QUnit and Jasmine are competent, well supported packages. However, because Jasmine currently has the edge in features, the rest of this article focusses on adding Jasmine tests to an ASP.NET MVC solution.</p>
<p><a name="vs2012"></a></p>
<h2>Visual Studio 2012 and Chutzpah</h2>
<p>Microsoft has made unit testing extensible in Visual Studio 2012. If you already use that version, you can use the Chutzpah package to make JavaScript unit testing a first class citizen inside Visual Studio (<a href="http://blogs.msdn.com/b/visualstudioalm/archive/2012/07/09/javascript-unit-tests-on-team-foundation-service-with-chutzpah.aspx">details</a>).</p>
<p>The rest of this article assumes you use Visual Studio 2010, where things a bit more complicated.</p>
<p><a name="vs2010"></a></p>
<h2>Integrating Jasmine unit tests in Visual Studio 2010 and TFS 2010</h2>
<p>To make this work, we can take this approach:</p>
<ol>
<li>Install Jasmine and add JavaScript unit tests.
<p>&nbsp;</p>
This allows us to run the tests inside a browser, which is good. However, we also want to run those tests as part of a unit test inside Visual Studio, and have it fail that test when the JavaScript unit tests fail.</li>
<li>Install Chutzpah. This allows us to run the Jasmine unit tests from the command line. It will write a failure message to the console if the tests fail.</li>
<li>Create a unit test within Visual Studio that executes that command line. If the output contains the failure message, fail the test.</li>
</ol>
<p>Lets make this work. You will find a worked out example in the download.</p>
<h3>Install Jasmine and write some tests</h3>
<ol>
<li>I'm assuming you have a project that looks a bit like this:
<p>&nbsp;</p>
<pre>MyProject.Site
Controllers
Models
Views
Scripts
...
MyProject.Tests
Controllers
...
</pre>
</li>
<li><a href="https://github.com/pivotal/jasmine/downloads">Download Jasmine zip file</a></li>
<li>Unzip the file. This will result in a few directores and an html file. Create a new directory JsUnitTests in your Test project and move the Jasmine directories and files in there:
<pre>MyProject.Site
Controllers
Models
Views
Scripts
...
MyProject.Tests
Controllers
<span style="color: red;">JsUnitTests spec src lib SpecRunner.html</span>
...
</pre>
</li>
<li>spec contains demo tests, and src contains demo JavaScript code that the demo tests run against. Open SpecRunner.html in a browser to see Jasmine in action.</li>
<li>Remove the src directory. Update SpecRunner.html with script tags that load the actual JavaScript code you want to test.
<p>&nbsp;</p>
Use relative paths in your script tags, not absolute paths. That makes it so much easier if you ever want to move or copy your solution. And especially if you use TFS Build.</li>
<li>Get rid of the demo tests in spec and write a few tests of your own against your own code. An excellent tutorial on writing Jasmine tests is on the <a href="http://pivotal.github.com/jasmine/">Jasmine home page</a>. Update SpecRunner.html with script tags that load your tests.</li>
<li>Open SpecRunner.html again in a browser to see whether your code passes.</li>
<li>Make sure that all your new files have been included in the project and that they are checked in.</li>
</ol>
<h3>Install Chutzpah</h3>
<p><a href="http://nuget.org/packages/Chutzpah">Chutzpah</a> allows you to run JavaScript unit tests from the command line.</p>
<ol>
<li><a href="http://docs.nuget.org/docs/start-here/installing-nuget">Install NuGet</a> if you haven't done so before.</li>
<li>In Visual Studio, open the Package Manager Console - click Tools | Library Package Manager | Package Manager Console.</li>
<li>At the PM&gt; prompt, enter
<pre>Install-Package Chutzpah
</pre>
<p>This creates a new packages directory in your solution and installs the Chutzpah files in there. At the time of writing, Chutzpah was at version 1.4.2, which results in:</p>
<pre><span style="color: red;">packages Chutzpah.1.4.2 tools chutzpah.console.exe ...</span>
MyProject.Site
Controllers
Models
Views
Scripts
...
MyProject.Tests
Controllers
JsUnitTests
spec
lib
SpecRunner.html
...
</pre>
</li>
<li>Now you can run the Jasmine tests from the command line (update this if the Chutzpah version number has changed):
<pre>cd &lt;directory containing SpecRunner.html&gt;
..\..\packages\Chutzpah.1.4.2\tools\chutzpah.console.exe SpecRunner.html
</pre>
</li>
</ol>
<h3>Create unit test inside Visual Studio</h3>
<p>Finally, we'll create a unit test that executes the command line you just saw.</p>
<ol>
<li>Install the NuGet package <a href="https://nuget.org/packages/ExecConsoleProgram/">ExecConsoleProgram</a>. This makes it easy to run a console program from managed code, such as a unit test. Open the PM&gt; prompt again and enter:
<pre>Install-Package ExecConsoleProgram
</pre>
<p>&nbsp;</p>
NuGet stores package information in a packages directory in the root of your solution (where you .sln is located). Be sure to check in that packages directory, so the build controller can access it when you do a TFS Build.</li>
<li>Add a reference to the ExecConsoleProgram dll to your Tests project. You'll find the dll in the packages directory.</li>
<li>To your JsUnitTests directory, add a new unit test - right click JsUnitTests | Add | New Test | Basic Unit Test</li>
<li>Make it look like this:
<pre>[TestMethod]
public void JsTest()
{
string stdOut;
string stdErr;
try
{
// Correct paths when running unit tests in Visual Studio
ExecConsoleProgram.ConsoleProgram.Execute(
@"..\..\JsUnitTests",
@"..\..\..\packages\Chutzpah.1.4.2\tools\chutzpah.console.exe",
@"SpecRunner.html",
out stdOut, out stdErr);
}
catch (System.ComponentModel.Win32Exception)
{
// Correct paths when running unit tests as part of TFS Build
// &gt;&gt;&gt;&gt;&gt;&gt; In the code below, replace <span style="color: red;">JasmineMVC.Tests</span> with the name
// of your own test project.
ExecConsoleProgram.ConsoleProgram.Execute(
@"..\Sources\<span style="color: red;">JasmineMVC.Tests</span>\JsUnitTests",
@"..\Sources\packages\Chutzpah.1.4.2\tools\chutzpah.console.exe",
@"SpecRunner.html",
out stdOut, out stdErr);
}
Assert.IsFalse(stdOut.Contains("[FAIL]"));
Assert.IsFalse(stdOut.Contains("ERROR OCCURRED"));
Assert.IsTrue(string.IsNullOrEmpty(stdErr));
}
</pre>
<p>The Execute method runs the command line. It takes these parameters:</p>
<ul>
<li>Working directory. This is relative to the directory where the currently running assembly is located.</li>
<li>Path to the executable (relative to the directory where the currently running assembly is located).</li>
<li>Parameters to the executable (relative to working directory).</li>
<li>stdOut and stdErr receive the output of the program. As you see, the unit test fails if Jasmine reported that the tests failed or if something went wrong.</li>
</ul>
</li>
<li>Done! When you now run your unit tests from the Test View window, they'll run the JavaScript tests as well. Change one of your tests to force it to fail and see what happens.</li>
</ol>http://mattperdeck.com/post/Unit-testing-JavaScript-as-part-of-TFS-Build.aspx
http://mattperdeck.com/post/Unit-testing-JavaScript-as-part-of-TFS-Build.aspx#commenthttp://mattperdeck.com/post.aspx?id=c291a9d7-2b2d-43a6-940f-d72868907f15Wed, 11 Jul 2012 00:45:00 -0400JavaScriptmperdeckhttp://mattperdeck.com/pingback.axdhttp://mattperdeck.com/post.aspx?id=c291a9d7-2b2d-43a6-940f-d72868907f158http://mattperdeck.com/trackback.axd?id=c291a9d7-2b2d-43a6-940f-d72868907f15http://mattperdeck.com/post/Unit-testing-JavaScript-as-part-of-TFS-Build.aspx#commenthttp://mattperdeck.com/syndication.axd?post=c291a9d7-2b2d-43a6-940f-d72868907f15