JavaScript Tests & Linting

DEP:

0003

Author:

Trey Hunner

Implementation Team:

Trey Hunner

Shepherd:

Carl Meyer

Status:

Final

Type:

Process

Created:

2014-05-04

Abstract

Add unit tests and coding style consistency enforcement (linting) for the JavaScript code in the admin and gis contrib packages.

Specification

QUnit test framework

Jasmine is a BDD testing frameworks similar to Ruby’s RSpec. QUnit is a testing framework that uses a testing syntax similar to Django’s existing testing framework. Mocha is a testing framework with pluggable testing syntax options.

Any of these (especially QUnit or Mocha) could be a fine choice for Django. This DEP selects QUnit because it is:

  • Popular enough to provide some assurance of future maintenance (used by jQuery, Backbone.js, and Ember.js).

  • Similar syntax to the Python test suite (module/test/assert rather than describe/it/expect/should).

  • Easy to setup (like Jasmine and Mocha, it only requires a JS and HTML file).

  • Auto-resets the #qunit-fixture element, providing easy isolation for DOM-related tests (and much of Django’s JS code is DOM-related).

Blanket for code coverage

Istanbul and Blanket.js are both popular JavaScript code coverage tools. Using Istanbul in a web browser requires using a Node.js command-line tool to generate Istanbul-wrapped test files. Blanket.js can inject itself into your code directly from a web browser and therefore does not require generating new test files.

Blanket.js should be used so that code coverage can be verified in a web browser without requiring Node.js.

EditorConfig for code style

The JavaScript files currently use a variety of code styles. As an example, some files use tabs for indentation, some use 4 spaces, and some use 2 spaces. Fortunately, each file is fairly self-consistent. Unfortunately, this variety of styles makes manual code style enforcement difficult.

EditorConfig should be used to document the desired code style and maintain this style while editing code.

ESLint for code linting

Linting code is particularly important in JavaScript due to certain hazardous language features. JSHint is a popular JavaScript linter which is based on the less-customizable JSLint tool. ESLint is a very customizable and unopinionated JavaScript linter which also includes code style checking.

ESLint should be used because:

  • It is customizable (unlike JSLint).

  • It defaults to a good set of community standards.

  • It can enforce a wider range of code style preferences.

Migration Path

The proposed migration path:

  1. Add a package.json file and a Gruntfile.js and introduce command-line QUnit tests with Blanket.js for code coverage.

  2. Add a few easy-to-implement tests to start (low-hanging fruit).

  3. Add ESLint and update code to conform to a style dictated in a .eslintrc file.

  4. Add EditorConfig for editor enforcement of code style guide.

  5. Document process used to run the tests from the command line and within a browser.

  6. Setup CI server to run the tests.

Running tests in a web browser is as easy as either:

  1. Opening ./js_tests/tests.html in your web browser (simplest case).

  2. Executing python -m http.server (python -m SimpleHTTPServer on Python 2) and opening http://localhost:8000/js_tests/tests.html in your web browser (needed for code coverage reporting).

Running tests via HTTP is required to run Blanket.js in the browser due to cross-origin resource sharing rules.

Steps to run tests from the command-line (locally or on the CI server):

  1. Install Node.js and NPM.

  2. Run npm install to install Node dependencies.

  3. Run npm test to run the tests and see results, including code coverage.

Motivation

Django admin and gis contrib packages contain JavaScript code without any unit tests. Django has functional tests which execute some of the JavaScript code, but functional tests are not good enough. Not all JavaScript code can be tested without forcing the execution of low-level browser events.

Rationale

A native JavaScript test framework is one that can be run without any Python code, either in the browser or from the command line.

The JavaScript code can be tested independently of the Python code. Therefore, the JavaScript and Python tests do not need to be intertwined.

Arguments for

  • Easier for a developer new to Django’s JavaScript testing practices.

  • Tests can be run manually from a web browser without any need for Node.js.

  • Creating tests only requires updating/creating a JavaScript file and updating an HTML file (no need to alter a py file).

  • The JS community maintains a reliable set of testing tools. Creating custom tools would require maintenance which no one has volunteered to do.

Arguments against

  • Executing automated tests on a continuous integration server without a Python wrapper will require Node.js and PhantomJS.

  • JavaScript tests must be executed separately from Python tests (./runtests.py will only execute Python tests).

The requirement of Node.js should not prove burdensome because:

  • Running JS tests locally only requires opening an HTML file in a web browser.

  • JSHint (a popular JS linter) also requires Node.js and therefore Node.js may already be installed locally.

Reference Implementation

Pull requests #4573 and #4577 implement all suggested changes in this DEP.