JSLint in my unit tests - javascript

I code according to JSLint standards (excluding a couple of options) and I thought it might be a good idea to work this into my in-browser unit tests so that I don't accidentally commit anything which doesn't pass it. I'm using QUnit, but the same can probably be applied to any in-browser testing framework.
I tried this code first of all:
test("code passes JSLint", function () {
var i, options;
options = {
browser : true,
plusplus : true,
unparam : true,
maxlen : 120,
indent : 4
};
// in QUnit `ok` is the equivalent of `assertTrue`
ok(JSLINT(this.code, options));
// Help me out a bit if it fails
for (i = 0; i < JSLINT.errors.length; i++) {
console.log(JSLINT.errors[i].line + ': ' + JSLINT.errors[i].reason);
}
});
Edit: Forgot to mention, I declared in the setup that this.code = myFunction.toString();.
Which works great in Chrome, so I committed and continued merrily coding. When I tried it in FF, I found that FF seems to strip all the white-space out of functions when it converts them into strings, so it fails.
I'm coding and testing locally, so using AJAX to download the JS file isn't really an option. Can anyone think of a better option, or is this just a complete waste of time? (Ignoring the benefits or drawbacks of JSLint as a whole please... that's for a different discussion)

What is your development environment? Jslint can be integrated into common IDEs like Eclipse and (I'm pretty sure) Visual Studio. I think that would be a better option then putting it into your unit tests even if it worked perfectly in the unit tests.
Otherwise to stick with the unit test approach maybe you could put a conditional in to only run the Jslint test if in Chrome - the things Jslint checks for don't need to be tested in multiple browsers.

Oddly, I'm seeing the opposite behavior (fails in chrome[v17.0.x], passes in FF[v10.0.1]). Either way, another solution is to tell JSLINT to not worry about the white spaces with white:true
Your code in coffeescript, setting JSLINT to ignore whitespace:
<script src="http://coffeescript.org/extras/coffee-script.js"></script>
<script src="https://github.com/douglascrockford/JSLint/raw/master/jslint.js"></script>
<script src="http://code.jquery.com/qunit/qunit-1.10.0.js"></script>
<script>
function myCode(){window.console.log("I have no whitespace");}
</script>
<script type="text/coffeescript">
test "Code is LINT'able", ()->
options =
browser:true
white:true #don't worry about whitespace
sloppy:true #allow missing "use strict"
ok JSLINT(myCode.toString(), options)
JSLINT.errors.forEach (error)->
console.log "#{error.line}: #{error.reason}"
</script>

Related

Need a quick JavaScript/jQuery Validation

I have a 17000 line page with lots of HTML/JavaScript/jQuery and it's always frustrating when I make a typo and there's no clue when the page loads into the browser what the problem is. It just - doesn't do anything. Consider the patch of code below, for example, where the third line terminates with a ' instead of a ; .
$(document).on('click', 'input#answer_chkbx', function(e) {
if(e.target.checked){
x$ = g.currentElement$.find('.cellContent')'
g.currentElement$.addClass('answerBox')
.css('background-color','tansparent')
.height(25)
.width(150);
}
});
There should be something that runs through the code and finds that immediately.
Is there a way to check for things like this?
Thanks
You might consider a tool such as JSLint or JSHint.
For offline coding you can use Netbeans IDE or Dreamweaver

somewhat odd javascript code works in all major browsers, but fails with phantomjs/qunit

I work on an app with a javascript/html front-end and a back-end REST service. I mostly work on the back end service, but I'm attempting to add javascript unit tests to the build. I had someone help me with the javascript testing framework setup, using phantomjs, qunit, and jstestrunner, all referenced from Maven.
I wrote a trivial unit test for a module (we'll call it "data.daily.js") that begins like this:
Data.Daily = new Function();
Data.Daily.prototype = {
Just to be clear, this code runs every day in production, and appears to work fine in all major browsers (FF, IE, and Chrome).
The test looks like this:
requirejs.config({ shim: { 'data.daily': ['config'] } });
require(['data.daily'], function() {
'use strict';
module('data.daily');
test('data.daily.test.initialize', function() {
var dataDaily = new Data.Daily();
dataDaily.initialize(Config.AJAX_DAILY_DATA_BASE_URL, Config.MOCKDATA_AJAX_DAILY_DATA_BASE_URL);
deepEqual(dataDaily.getData(), {}, "object is \"" + JSON.stringify(dataDaily.getData()) + "\", but it should be empty object");
});
});
When I run this test, it fails like this:
ReferenceError: Can't find variable: Data, source: http://localhost:9080/data.daily.js:5
[data.daily] data.daily.test.initialize: failed: 1 passed: 0
Died on test #1 at http://localhost:9080/js/qunit.js:425
at http://localhost:9080/js/data.daily.test.js:17
at http://localhost:9080/js/require.js:1682
at http://localhost:9080/js/require.js:983
at http://localhost:9080/js/require.js:1194
at http://localhost:9080/js/require.js:129
at http://localhost:9080/js/require.js:1237
at each (http://localhost:9080/js/require.js:58)
at http://localhost:9080/js/require.js:1238
at http://localhost:9080/js/require.js:1043
at http://localhost:9080/js/require.js:1224
at http://localhost:9080/js/require.js:882
at callGetModule (http://localhost:9080/js/require.js:1249)
at http://localhost:9080/js/require.js:1578
at http://localhost:9080/js/require.js:1703: Can't find variable: Data, source: ReferenceError: Can't find variable: Data
The only way I can find to get this test working is to change "data.daily.js" in this way, adding a line before the existing lines:
var Data = {};
Data.Daily = new Function();
Data.Daily.prototype = {
Now I have to say that this looks logical to me, but the fact remains that the existing code works fine in all the major browsers. This code only started failing when referenced from the test.
Note that I also tried changing the test script instead, adding the "var Data = {}" line before the "var dataDaily = new Data.Daily()" line, but that had no effect.
So, can anyone explain what is going on here? Why does the original code work if it fails in the test. Is there something funky about how "require.js" works that makes this happen? Why didn't the test work by adding the line in the test, instead of the CUT (code under test)?
Ok, I've managed to resolve this.
The assignment is actually present in the existing production code, I just didn't think to look in ".html" files for it before. When I didn't find it in ".js" files, I thought something else was going on.
The reason it didn't work to put the line in the test script instead was because I was putting the line in the wrong place. The error actually occurs at configuration time, not when the test itself is executed, so the assignment had to be before the "requirejs.config()" call. Now the test works, without having to modify the CUT.

JSHint chaining method calls

I have the following code in an Angular app:
'use strict';
angular.module('fooApp')
.controller('FooCtrl', function ($scope) {
});
When I run JSHint (with indent set to 4) on this code, I get the following error:
[L6:C5] W015: Expected '}' to have an indentation at 1 instead at 5.
});
How do I get JSHint to allow me to keep my chaining indentation?
Update
I found that if I add a body to the FooCtrl function like this:
'use strict';
angular.module('fooApp')
.controller('FooCtrl', function ($scope) {
$scope.foo = {};
});
Then it does pass JSHint. Anyone know why?
I don't believe there is a way to do it. The whitespace checking in JSHint is fairly static, you get on or off, no configuration. There's an open bounty to add some configuration, but nothing seems to be in the works (at least for your situation).
https://github.com/jshint/jshint/issues/28
You'll either have to just ignore it or turn off the whitespace check.
simply said: you don't. Either you remove all indentation checking from your config file, or you match the crockford's recommandation. There's an open bug about giving more flexible rules, but it has to be implemented.
Having submitted code to JSHint, it would not be hard to implement a more flexible way to check whitespaces. Except that there are a lot of cases where it has to be checked... The main problem is to find an intelligent way to fine tune your indentation preferences.

IE8 and SmoothGallery: Just getting script errors

I am using Jon Designs SmoothGallery on a website for one of my clients. The only problem is that I get a script error in IE8 when trying to go to the next image. I have checked this gallery in all major browser, and yet again, IE is the one where it doesn't work.
What a surprise.
I have been trying to find out what the problem was, but I only get an error in the eval-function of the mootools library which the gallery is based on, so no luck there.
Is there any way to set a "All-Exceptions Breakpoint", simular to setting one in Xcode, so that I could find the code that it actually hangs up on? Or does anyone know what is actually going on here?
The site is http://internetvolk.de/tische and the JS file responsible for the gallery is http://internetvolk.de/typo3conf/ext/rgsmoothgallery/res/scripts/jd.gallery1010.js
Thank you in advance,
Tobias Timpe
you are using a really old version of mootools - 0.89 or 1.0, if I am not mistaken. it's eval packed via dean edwards' PACKER, which makes it particularly hard to debug.
the instantiation code itself is somewhat awful and monkey patched:
function startGallery17() {
if (window.gallery17) {
try {
var myGallery17 = new gallery($('myGallery17'), {
timed: false,
showArrows: true,
showCarousel: false,
textShowCarousel: 'Thumbnails',
embedLinks: false,
lightbox: true
});
var mylightbox = new LightboxSmoothgallery();
} catch (error) {
window.setTimeout("startGallery17();", 2500);
}
} else {
window.gallery17 = true;
if (this.ie) {
window.setTimeout("startGallery17();", 3000);
} else {
window.setTimeout("startGallery17();", 100);
}
}
}
window.onDomReady(startGallery17);
the use of strings in setTimeouts causes evals here also, the try blocks will prevent you from getting any useful error feedback either.
if you want to get to the bottom of this, get rid of these hacks, load up say, this version of mootools - https://ajax.googleapis.com/ajax/libs/mootools/1.11/mootools.js - still very old but newer than yours, or if possible, try 1.12 and see if debugging becomes easier.
then get rid of the try/catch blocks, use window.addEvent('domready', fn) to start your code - or if reliant on images being loaded, consider window.addEvent('load', fn) instead.
if all you do on the page is stack up some images with a next/previous, why not use latest mootools with a new plugin - there is NOTHING good about what you have picked.
eg, read this tutorial I wrote on how to do it: http://fragged.org/tutorial-write-a-small-content-slider-class-in-mootools-and-extend-it_1321.html or look on the forge: http://mootools.net/forge/ for something packaged
should be fine with 1.4.5 (latest).
keep in mind the version you are currently using will likely break ie9 and latest gecko

Exclude debug JavaScript code during minification

I'm looking into different ways to minify my JavaScript code including the regular JSMin, Packer, and YUI solutions. I'm really interested in the new Google Closure Compiler, as it looks exceptionally powerful.
I noticed that Dean Edwards packer has a feature to exclude lines of code that start with three semicolons. This is handy to exclude debug code. For instance:
;;; console.log("Starting process");
I'm spending some time cleaning up my codebase and would like to add hints like this to easily exclude debug code. In preparation for this, I'd like to figure out if this is the best solution, or if there are other techniques.
Because I haven't chosen how to minify yet, I'd like to clean the code in a way that is compatible with whatever minifier I end up going with. So my questions are these:
Is using the semicolons a standard technique, or are there other ways to do it?
Is Packer the only solution that provides this feature?
Can the other solutions be adapted to work this way as well, or do they have alternative ways of accomplishing this?
I will probably start using Closure Compiler eventually. Is there anything I should do now that would prepare for it?
here's the (ultimate) answer for closure compiler :
/** #const */
var LOG = false;
...
LOG && log('hello world !'); // compiler will remove this line
...
this will even work with SIMPLE_OPTIMIZATIONS and no --define= is necessary !
Here's what I use with Closure Compiler. First, you need to define a DEBUG variable like this:
/** #define {boolean} */
var DEBUG = true;
It's using the JS annotation for closure, which you can read about in the documentation.
Now, whenever you want some debug-only code, just wrap it in an if statement, like so:
if (DEBUG) {
console.log("Running in DEBUG mode");
}
When compiling your code for release, add the following your compilation command: --define='DEBUG=false' -- any code within the debug statement will be completely left out of the compiled file.
A good solution in this case might be js-build-tools which supports 'conditional compilation'.
In short you can use comments such as
// #ifdef debug
var trace = debug.getTracer("easyXDM.Rpc");
trace("constructor");
// #endif
where you define a pragma such as debug.
Then when building it (it has an ant-task)
//this file will not have the debug code
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.js"/>
//this file will
<preprocess infile="work/easyXDM.combined.js" outfile="work/easyXDM.debug.js" defines="debug"/>
Adding logic to every place in your code where you are logging to the console makes it harder to debug and maintain.
If you are already going to add a build step for your production code, you could always add another file at the top that turns your console methods into noop's.
Something like:
console.log = console.debug = console.info = function(){};
Ideally, you'd just strip out any console methods, but if you are keeping them in anyway but not using them, this is probably the easiest to work with.
If you use the Closure Compiler in Advanced mode, you can do something like:
if (DEBUG) console.log = function() {}
Then the compiler will remove all your console.log calls. Of course you need to --define the variable DEBUG in the command line.
However, this is only for Advanced mode. If you are using Simple mode, you'll need to run a preprocessor on your source file.
Why not consider the Dojo Toolkit? It has built-in comment-based pragma's to include/exclude sections of code based on a build. Plus, it is compatible with the Closure Compiler in Advanced mode (see link below)!
http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t
Even though its an old question. I stumbled upon the same issue today and found that it can be achieved using CompilerOptions.
I followed this thread.
We run the compiler, from Java, on our server before sending the code to the client. This worked for us in Simple mode.
private String compressWithClosureCompiler(final String code) {
final Compiler compiler = new Compiler();
final CompilerOptions options = new CompilerOptions();
Logger.getLogger("com.google.javascript.jscomp").setLevel(Level.OFF);
if (compressRemovesLogging) {
options.stripNamePrefixes = ImmutableSet.of("logger");
options.stripNameSuffixes = ImmutableSet.of("debug", "dev", "info", "error",
"warn", "startClock", "stopClock", "dir");
}
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
final JSSourceFile extern = JSSourceFile.fromCode("externs.js", "");
final JSSourceFile input = JSSourceFile.fromCode("input.js", code);
compiler.compile(extern, input, options);
return compiler.toSource();
}
It will remove all the calls to logger.debug, logger.dev...etc.etc
If you're using UglifyJS2, you can use the drop_console argument to remove console.* functions.
I use this in my React apps:
if (process.env.REACT_APP_STAGE === 'PROD')
console.log = function no_console() {};
In other words, console.log will return nothing on prod enviroment.
I am with #marcel-korpel. Isn't perfect but works. Replace the debug instructions before minification. The regular expression works in many places. Watch out unenclosed lines.
/console\.[^;]*/gm
Works on:
;;; console.log("Starting process");
console.log("Starting process");
console.dir("Starting process");;;;;
console.log("Starting "+(1+2)+" processes"); iamok('good');
console.log('Message ' +
'with new line'
);
console.group("a");
console.groupEnd();
swtich(input){
case 1 : alert('ok'); break;
default: console.warn("Fatal error"); break;
}
Don't works:
console.log("instruction without semicolon")
console.log("semicolon in ; string");
I haven't looked into minification so far, but this behaviour could be accomplished using a simple regular expression:
s/;;;.*//g
This replaces everything in a line after (and including) three semicolons with nothing, so it's discarded before minifying. You can run sed (or a similar tool) before running your minification tool, like this:
sed 's/;;;.*//g' < infile.js > outfile.js
BTW, if you're wondering whether the packed version or the minified version will be 'better', read this comparison of JavaScript compression methods.
I've used following self-made stuf:
// Uncomment to enable debug messages
// var debug = true;
function ShowDebugMessage(message) {
if (debug) {
alert(message);
}
}
So when you've declared variable debug which is set to true - all ShowDebugMessage() calls would call alert() as well. So just use it in a code and forget about in place conditions like ifdef or manual commenting of the debug output lines.
I was searching for a built-in option to do this. I have not found that yet, but my favorite answer also does not require any changes to existing source code. Here's an example with basic usage.
Assume HTML file test.html with:
<html>
<script src="hallo.js"></script>
</html>
And hallo.js with:
sayhi();
function sayhi()
{
console.log("hallo, world!");
}
We'll use a separate file, say noconsole.js, having this from the linked answer:
console.log = console.debug = console.info = function(){};
Then we can compile it as follows, bearing in mind that order matters, noconsole.js must be placed first in the arguments:
google-closure-compiler --js noconsole.js hallo.js --js_output_file hallo.out.js
If you cat hallo.out.js you'd see:
console.log=console.debug=console.info=function(){};sayhi();function sayhi(){console.log("hallo, world!")};
And if I test with mv hallo.out.js hallo.js and reload the page, I can see that the console is now empty.
Hope this clarifies it. Note that I have not yet tested this in the ideal mode of compiling all the source code with ADVANCED optimizations, but I'd expect it to also work.

Categories