JSHint integration

JSHint helps enforcing coding standards for the javascript files similarly to
pep8. It is integrated with the existing qUnit tests with the qhint library.
This approach has the advantage of running every time the selenium test suite
runs and does not introduce dependency to Node.js.

Partially implements: blueprint js-coding-style-checks

Change-Id: I77e75e7cdd5fd3426b9b981c4e0df5d30ef7b196
This commit is contained in:
Imre Farkas 2013-08-06 15:05:48 +02:00 committed by Radomir Dopieralski
parent 494b7f8b20
commit 7711a78ac4
5 changed files with 61601 additions and 2 deletions
horizon
static/horizon
templates/horizon
test/tests

File diff suppressed because it is too large Load Diff

@ -0,0 +1,88 @@
/*! qHint 1.1 | http://gyoshev.mit-license.org */
(function() {
var qHint =
window.jsHintTest =
window.qHint =
function qHint(name, sourceFile, options, globals) {
if (sourceFile === undefined || typeof(sourceFile) == "object") {
// jsHintTest('file.js', [options])
globals = options;
options = sourceFile;
sourceFile = name;
}
return asyncTest(name, function() {
qHint.sendRequest(sourceFile, function(req) {
start();
if (req.status == 200) {
qHint.validateFile(req.responseText, options, globals);
} else {
ok(false, "HTTP error " + req.status +
" while fetching " + sourceFile);
}
});
});
};
qHint.validateFile = function (source, options, globals) {
var i, len, err;
if (JSHINT(source, options, globals)) {
ok(true);
return;
}
for (i = 0, len = JSHINT.errors.length; i < len; i++) {
err = JSHINT.errors[i];
if (!err) {
continue;
}
ok(false, err.reason +
" on line " + err.line +
", character " + err.character);
}
};
var XMLHttpFactories = [
function () { return new XMLHttpRequest(); },
function () { return new ActiveXObject("Msxml2.XMLHTTP"); },
function () { return new ActiveXObject("Msxml3.XMLHTTP"); },
function () { return new ActiveXObject("Microsoft.XMLHTTP"); }
];
function createXMLHTTPObject() {
for (var i = 0; i < XMLHttpFactories.length; i++) {
try {
return XMLHttpFactories[i]();
} catch (e) {}
}
return false;
}
// modified version of XHR script by PPK
// http://www.quirksmode.org/js/xmlhttp.html
// attached to qHint to allow substitution / mocking
qHint.sendRequest = function (url, callback) {
var req = createXMLHTTPObject();
if (!req) {
return;
}
var method = "GET";
req.open(method,url,true);
req.onreadystatechange = function () {
if (req.readyState != 4) {
return;
}
callback(req);
};
if (req.readyState == 4) {
return;
}
req.send();
};
})();

@ -0,0 +1,61 @@
horizon.addInitFunction(function () {
module('Coding Style (jsHint)');
test('jsHint', function () {
expect(0);
config = {
// Warnings reported by JSHint. Suppressing for now...
'asi': true, // missing semicolons
'-W004': true, // already defined
'-W009': true, // array literal notation [] is preferrable
'-W014': true, // bad line breaking
'-W018': true, // confusing use
'-W038': true, // out of scope
'-W041': true, // use '!==' or '===' for comparison
'-W046': true, // extra leading zeros
'-W069': true, // better written in dot notation
'-W065': true, // missing radix parameter
'-W075': true, // duplicate key
'-W080': true, // it's not necessary to initialize to 'undefined'
'-W093': true, // conditional instead of an assignment
// Proposed set of rules
//'camelcase' : true,
//'indent': 2,
//'undef': true,
//'quotmark': 'single',
//'maxlen': 80,
//'trailing': true,
//'curly': true
};
jsHintTest('horizon.communication.js', '/static/horizon/js/horizon.communication.js', config);
jsHintTest('horizon.conf.js', '/static/horizon/js/horizon.conf.js', config);
jsHintTest('horizon.cookies.js', '/static/horizon/js/horizon.cookies.js', config);
jsHintTest('horizon.d3linechart.js', '/static/horizon/js/horizon.d3linechart.js', config);
jsHintTest('horizon.d3piechart.js', '/static/horizon/js/horizon.d3piechart.js', config);
jsHintTest('horizon.firewalls.js', '/static/horizon/js/horizon.firewalls.js', config);
jsHintTest('horizon.forms.js', '/static/horizon/js/horizon.forms.js', config);
jsHintTest('horizon.heattop.js', '/static/horizon/js/horizon.heattop.js', config);
jsHintTest('horizon.instances.js', '/static/horizon/js/horizon.instances.js', config);
jsHintTest('horizon.js', '/static/horizon/js/horizon.js', config);
jsHintTest('horizon.membership.js', '/static/horizon/js/horizon.membership.js', config);
jsHintTest('horizon.messages.js', '/static/horizon/js/horizon.messages.js', config);
jsHintTest('horizon.modals.js', '/static/horizon/js/horizon.modals.js', config);
jsHintTest('horizon.networktopology.js', '/static/horizon/js/horizon.networktopology.js', config);
jsHintTest('horizon.quota.js', '/static/horizon/js/horizon.quota.js', config);
jsHintTest('horizon.tables.js', '/static/horizon/js/horizon.tables.js', config);
jsHintTest('horizon.tabs.js', '/static/horizon/js/horizon.tabs.js', config);
jsHintTest('horizon.templates.js', '/static/horizon/js/horizon.templates.js', config);
jsHintTest('horizon.users.js', '/static/horizon/js/horizon.users.js', config);
jsHintTest('horizon.utils.js', '/static/horizon/js/horizon.utils.js', config);
jsHintTest('tests/jshint.js', '/static/horizon/tests/jshint.js', config);
jsHintTest('tests/messages.js', '/static/horizon/tests/messages.js', config);
jsHintTest('tests/modals.js', '/static/horizon/tests/modals.js', config);
jsHintTest('tests/tables.js', '/static/horizon/tests/tables.js', config);
jsHintTest('tests/templates.js', '/static/horizon/tests/templates.js', config);
});
});

@ -6,6 +6,8 @@
<link rel="stylesheet" href="{{ STATIC_URL }}horizon/lib/qunit/qunit.css" type="text/css" media="screen">
<script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/qunit/qunit.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/jshint-2.3.0.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/qhint.js"></script>
{% include "horizon/_conf.html" %}
{% comment %}Load test modules here.{% endcomment %}
@ -13,6 +15,7 @@
<script type="text/javascript" src="{{ STATIC_URL }}horizon/tests/modals.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/tests/templates.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/tests/tables.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}horizon/tests/jshint.js"></script>
{% comment %}End test modules.{% endcomment %}
{% include "horizon/_scripts.html" %}

@ -20,7 +20,7 @@ from horizon.test import helpers as test
class BrowserTests(test.SeleniumTestCase):
def test_qunit(self):
self.selenium.get("%s%s" % (self.live_server_url, "/qunit/"))
wait = self.ui.WebDriverWait(self.selenium, 10)
wait = self.ui.WebDriverWait(self.selenium, 20)
def qunit_done(driver):
text = driver.find_element_by_id("qunit-testresult").text
@ -28,4 +28,9 @@ class BrowserTests(test.SeleniumTestCase):
wait.until(qunit_done)
failed = self.selenium.find_element_by_class_name("failed")
self.assertEqual(int(failed.text), 0)
if int(failed.text) > 0:
filename = self.selenium.find_element_by_css_selector(
"#qunit-tests > li.fail span.test-name").text
message = self.selenium.find_element_by_css_selector(
"#qunit-tests > li.fail span.test-message").text
self.fail('%s: %s' % (filename, message))