'use strict'; var fs = require('fs'); var path = require('path'); var bootlint = require('../src/bootlint.js'); function _fixtureNameToFilepath(name) { return path.join(__dirname, '/fixtures/', name); } function utf8Fixture(name) { return fs.readFileSync(_fixtureNameToFilepath(name), {encoding: 'utf8'}); } function utf16Fixture(name) { return fs.readFileSync(_fixtureNameToFilepath(name), {encoding: 'utf16le'}); } function lintHtml(html, disabledIds) { var lints = []; var reporter = function (lint) { lints.push(lint.message); }; bootlint.lintHtml(html, reporter, disabledIds || []); return lints; } /* ======== A Handy Little Nodeunit Reference ======== https://github.com/caolan/nodeunit Test methods: test.expect(numAssertions) test.done() Test assertions: test.ok(value, [message]) test.deepEqual(actual, expected, [message]) test.notDeepEqual(actual, expected, [message]) test.strictEqual(actual, expected, [message]) test.notStrictEqual(actual, expected, [message]) test.throws(block, [error], [message]) test.doesNotThrow(block, [error], [message]) test.ifError(value) */ exports.bootlint = { 'HTML5 DOCTYPE': function (test) { test.expect(4); test.deepEqual(lintHtml(utf8Fixture('doctype/missing.html')), ['Document is missing a DOCTYPE declaration'], 'should complain when no doctype declaration is present.'); test.deepEqual(lintHtml(utf8Fixture('doctype/html4.html')), ['Document declares a non-HTML5 DOCTYPE'], 'should complain when the HTML4 doctype is used.'); test.deepEqual(lintHtml(utf8Fixture('doctype/html5-normal.html')), [], 'should not complain when the normal simple HTML5 doctype is used.'); test.deepEqual(lintHtml(utf8Fixture('doctype/html5-legacy.html')), [], 'should not complain when the legacy-compatibility HTML5 doctype is used.'); test.done(); }, 'disabling lint checks': function (test) { test.expect(1); test.deepEqual(lintHtml(utf8Fixture('bs-v2.html'), ['E002', 'E013']), [], 'should complain when Bootstrap v2 grid classes are present.'); test.done(); }, 'UTF-8 charset meta tag': function (test) { test.expect(4); test.deepEqual(lintHtml(utf8Fixture('charset/utf8.html')), [], 'should not complain when UTF-8 charset tag is present.'); test.deepEqual(lintHtml(utf8Fixture('charset/utf8-meta.html')), [], 'should not complain when UTF-8 charset is present in Content-Type tag'); test.deepEqual(lintHtml(utf8Fixture('charset/missing.html')), ['`` is missing UTF-8 charset `` tag'], 'should complain when charset tag is missing.'); test.deepEqual(lintHtml(utf16Fixture('charset/not-utf8.html')), ['charset `` tag is specifying a legacy, non-UTF-8 charset'], 'should complain when tag specifies non-UTF-8 charset.'); test.done(); }, 'X-UA-Compatible': function (test) { test.expect(3); test.deepEqual(lintHtml(utf8Fixture('x-ua-compatible/present.html')), [], 'should not complain when X-UA-Compatible tag is present.'); test.deepEqual(lintHtml(utf8Fixture('x-ua-compatible/lowercase.html')), [], 'should not complain when X-UA-Compatible tag is present but lowercased.'); test.deepEqual(lintHtml(utf8Fixture('x-ua-compatible/missing.html')), ['`` is missing X-UA-Compatible `` tag that disables old IE compatibility modes'], 'should complain when X-UA-Compatible tag is missing.'); test.done(); }, 'Bootstrap v2': function (test) { test.expect(1); test.deepEqual(lintHtml(utf8Fixture('bs-v2.html')), [ 'Found one or more uses of outdated Bootstrap v2 `.spanN` grid classes', 'Only columns (`.col-*-*`) may be children of `.row`s' ], 'should complain when Bootstrap v2 grid classes are present.'); test.done(); }, 'rows outside containers': function (test) { test.expect(6); test.deepEqual(lintHtml(utf8Fixture('containers/fixed.html')), [], 'should not complain when rows are descendants of fixed containers.'); test.deepEqual(lintHtml(utf8Fixture('containers/fluid.html')), [], 'should not complain when rows are descendants of fluid containers.'); test.deepEqual(lintHtml(utf8Fixture('containers/columns.html')), [], 'should not complain when rows are children of columns.'); test.deepEqual(lintHtml(utf8Fixture('containers/missing.html')), ['Found one or more `.row`s that were not children of a grid column or descendants of a `.container` or `.container-fluid` or `.modal-body`'], 'should complain when a row is not a descendant of a container.'); test.deepEqual(lintHtml(utf8Fixture('containers/ancestor.html')), [], 'should not complain when rows are descendants (but not children) of containers.'); test.deepEqual(lintHtml(utf8Fixture('containers/row-inside-modal-body.html')), [], 'should not complain when rows are children of `.modal-body`.'); test.done(); }, 'nested containers': function (test) { test.expect(4); test.deepEqual(lintHtml(utf8Fixture('containers/nested-fixed-fixed.html')), ['Containers (`.container` and `.container-fluid`) are not nestable'], 'should complain when a container is within a container.'); test.deepEqual(lintHtml(utf8Fixture('containers/nested-fixed-fluid.html')), ['Containers (`.container` and `.container-fluid`) are not nestable'], 'should complain when a container is within a container.'); test.deepEqual(lintHtml(utf8Fixture('containers/nested-fluid-fluid.html')), ['Containers (`.container` and `.container-fluid`) are not nestable'], 'should complain when a container is within a container.'); test.deepEqual(lintHtml(utf8Fixture('containers/nested-fluid-fixed.html')), ['Containers (`.container` and `.container-fluid`) are not nestable'], 'should complain when a container is within a container.'); test.done(); }, 'viewport meta tag': function (test) { test.expect(2); test.deepEqual(lintHtml(utf8Fixture('viewport/present.html')), [], 'should not complain when viewport tag is present'); test.deepEqual(lintHtml(utf8Fixture('viewport/missing.html')), ['`` is missing viewport `` tag that enables responsiveness'], 'should complain when viewport tag is missing.'); test.done(); }, 'row and column classes on same element': function (test) { test.expect(1); test.deepEqual(lintHtml(utf8Fixture('grid/row-col-same-elem.html')), [ 'Found both `.row` and `.col-*-*` used on the same element', 'Columns (`.col-*-*`) can only be children of `.row`s or `.form-group`s' ], 'should complain when .row and .col-*-* are used on the same element.'); test.done(); }, 'row and container classes on same element': function (test) { test.expect(2); test.deepEqual(lintHtml(utf8Fixture('containers/fixed-row-same-elem.html')), ['Found one or more `.row`s that were not children of a grid column or descendants of a `.container` or `.container-fluid` or `.modal-body`'], 'should complain when .row and .container are used on the same element.'); test.deepEqual(lintHtml(utf8Fixture('containers/fluid-row-same-elem.html')), ['Found one or more `.row`s that were not children of a grid column or descendants of a `.container` or `.container-fluid` or `.modal-body`'], 'should complain when .row and .container-fluid are used on the same element.'); test.done(); }, 'remote modals': function (test) { test.expect(1); test.deepEqual(lintHtml(utf8Fixture('modal/remote.html')), ['Found one or more modals using the deprecated `remote` option'], 'should complain when remote modals are present.'); test.done(); }, 'jQuery': function (test) { test.expect(5); test.deepEqual(lintHtml(utf8Fixture('jquery/present.html')), [], 'should not complain when jQuery is present.'); test.deepEqual(lintHtml(utf8Fixture('jquery/jquery-plugin.html')), [], 'should not complain when jQuery & a plugin is present.'); test.deepEqual(lintHtml(utf8Fixture('jquery/old-url.html')), ['Found what might be an outdated version of jQuery; Bootstrap requires jQuery v1.9.1 or higher'], 'should complain about old version of jQuery based on URL'); test.deepEqual(lintHtml(utf8Fixture('jquery/missing.html')), ['Unable to locate jQuery, which is required for Bootstrap\'s JavaScript plugins to work'], 'should complain when jQuery appears to be missing.'); test.deepEqual(lintHtml(utf8Fixture('jquery/and_bs_js_both_missing.html')), ['Unable to locate jQuery, which is required for Bootstrap\'s JavaScript plugins to work; however, you might not be using Bootstrap\'s JavaScript'], 'should complain differently when jQuery appears to be missing but Bootstrap\'s JS is also missing.'); test.done(); }, 'bootstrap[.min].js': function (test) { test.expect(4); test.deepEqual(lintHtml(utf8Fixture('js/both.html')), ['Only one copy of Bootstrap\'s JS should be included; currently the webpage includes both bootstrap.js and bootstrap.min.js'], 'should complain when both bootstrap.js and bootstrap.min.js are included.'); test.deepEqual(lintHtml(utf8Fixture('js/one.html')), [], 'should not complain when only 1 of bootstrap.js and bootstrap.min.js is included.'); test.deepEqual(lintHtml(utf8Fixture('js/similar.html')), [], 'should not complain when only 1 of bootstrap.js and bootstrap.min.js is included but another JS file with "bootstrap" in its name is included.'); test.deepEqual(lintHtml(utf8Fixture('js/weird.html')), ['Only one copy of Bootstrap\'s JS should be included; currently the webpage includes both bootstrap.js and bootstrap.min.js'], 'should complain when both bootstrap.js and bootstrap.min.js are included, even when their URLs use fragments and query strings.'); test.done(); }, 'input groups with impermissible kind of form control': function (test) { test.expect(3); test.deepEqual(lintHtml(utf8Fixture('input-group/textarea.html')), ['`.input-group` contains a `