html-inspector
Advanced tools
Comparing version 0.5.1 to 0.6.0
{ | ||
"name": "html-inspector", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"main": "dist/html-inspector.js", | ||
@@ -5,0 +5,0 @@ "ignore": [ |
@@ -0,1 +1,6 @@ | ||
### 0.6.0 (November 9, 2013) | ||
* Add the ability to whitelist to every rule. | ||
* Fix a security warning in Firefox when trying to access properties of cross origin stylesheets. | ||
### 0.5.1 (August 31, 2013) | ||
@@ -2,0 +7,0 @@ |
17
FAQs.md
@@ -5,2 +5,3 @@ # FAQs | ||
- [Can HTML Inspector detect unclosed tags?](#can-html-inspector-detect-unclosed-tags) | ||
- [Why does HTML Inspector warn me about unused classes when those classes *are* found in my stylesheets?](#why-does-html-inspector-warn-me-about-unused-classes-when-those-classes-are-found-in-my-stylesheets) | ||
- [How can I get HTML Inspector to stop warning me about unused Modernizr classes?](#how-can-i-get-html-inspector-to-stop-warning-me-about-unused-modernizr-classes) | ||
@@ -26,2 +27,16 @@ - [I use Disqus and I'm getting warnings about obsolete attributes on the `<iframe>` element. How can I prevent those?](#i-use-disqus-and-im-getting-warnings-about-obsolete-attributes-on-the-iframe-element-how-can-i-prevent-those) | ||
<<<<<<< HEAD | ||
#### HTML Inspector warns me about using classes not found in any stylesheet, but those classes *are* in my stylesheets. What's going on? | ||
HTML Inspector creates its list of classes by looping through the `document.styleSheets` object. For most situations this works just fine, but if you have stylesheets loaded from a different domain, or you're using the `file:` protocol, most browsers report those stylesheets as empty. | ||
Unfortunately, there's no easy way around this. Your options are to either only use stylesheets on the same domain, whitelist the offending classes, or exclude the `unused-classes` rule. | ||
======= | ||
#### Why does HTML Inspector warn me about unused classes when those classes *are* found in my stylesheets? | ||
HTML Inspector creates its list of classes by looping through the `document.styleSheets` object. For most situations this works just fine, but if you have stylesheets loaded from a different domain, or you're using the `file:` protocol, most browsers report those stylesheets as empty. | ||
Unfortunately, there's no easy way around this. Your options are to either only use stylesheets on the same domain, whitelist the offending classes, or simply not use the `unused-classes` rule. | ||
>>>>>>> 689a1b44c795dda09711cd20ffd61b23ff481190 | ||
#### How can I get HTML Inspector to stop warning me about unused Modernizr classes? | ||
@@ -88,2 +103,2 @@ | ||
})(window)); | ||
``` | ||
``` |
@@ -84,2 +84,5 @@ module.exports = function(grunt) { | ||
mochacli: { | ||
options: { | ||
reporter: "spec" | ||
}, | ||
src: "test/classes/**/*.js" | ||
@@ -86,0 +89,0 @@ }, |
{ | ||
"name": "html-inspector", | ||
"title": "HTML Inspector", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "HTML Inspector is a code quality tool to help you and your team write better markup. It's written in JavaScript and runs in the browser, so testing your HTML has never been easier.", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
@@ -21,2 +21,6 @@ # HTML Inspector | ||
### News | ||
**The online hackathan site [ChallengePost](http://challengepost.com/) is sponsoring a competition to extend HTML Inspector. You can [register here](http://htmlinspector.challengepost.com) to enter. Submissions are due by November 13, 2013.** | ||
## Getting Started | ||
@@ -27,3 +31,3 @@ | ||
```html | ||
<script src="http://cdnjs.cloudflare.com/ajax/libs/html-inspector/0.4.1/html-inspector.js"></script> | ||
<script src="http://cdnjs.cloudflare.com/ajax/libs/html-inspector/0.5.1/html-inspector.js"></script> | ||
``` | ||
@@ -374,2 +378,2 @@ | ||
The FAQs section has grown rather large, so it has been moved to its own page. You can find the [full FAQs here](https://github.com/philipwalton/html-inspector/blob/master/FAQs.md). | ||
The FAQs section has grown rather large, so it has been moved to its own page. You can find the [full FAQs here](https://github.com/philipwalton/html-inspector/blob/master/FAQs.md). |
@@ -14,4 +14,3 @@ var Listener = require("./listener") | ||
// used to parse URLs | ||
, link = document.createElement("a") | ||
, isCrossOrigin = require("./utils/cross-origin") | ||
@@ -96,12 +95,2 @@ /** | ||
/** | ||
* Tests whether a URL is cross-origin | ||
* Same origin URLs must have the same protocol and host | ||
* (note: host include hostname and port) | ||
*/ | ||
function isCrossOrigin(url) { | ||
link.href = url | ||
return !(link.protocol == location.protocol && link.host == location.host) | ||
} | ||
/** | ||
* cross-origin iframe elements throw errors when being | ||
@@ -108,0 +97,0 @@ * logged to the console. |
@@ -5,2 +5,3 @@ var reClassSelector = /\.[a-z0-9_\-]+/ig | ||
, matches = require("dom-utils/src/matches") | ||
, isCrossOrigin = require("../utils/cross-origin") | ||
@@ -32,3 +33,6 @@ /** | ||
return styleSheets.reduce(function(classes, sheet) { | ||
return classes.concat(getClassesFromRuleList(toArray(sheet.cssRules))) | ||
// cross origin stylesheets don't expose their cssRules property | ||
return sheet.href && isCrossOrigin(sheet.href) | ||
? classes | ||
: classes.concat(getClassesFromRuleList(toArray(sheet.cssRules))) | ||
}, []) | ||
@@ -35,0 +39,0 @@ } |
@@ -775,10 +775,2 @@ var foundIn = require("../utils/string-matcher") | ||
function isWhitelistedElement(element) { | ||
return foundIn(element, spec.elementWhitelist) | ||
} | ||
function isWhitelistedAttribute(attribute) { | ||
return foundIn(attribute, spec.attributeWhitelist) | ||
} | ||
function getAllowedChildElements(parent) { | ||
@@ -790,3 +782,3 @@ var contents | ||
contents = elementData[parent].children | ||
contents = contents.indexOf("*") > -1 ? [] : contents.split(/\s*\;\s*/) | ||
contents = contents.indexOf("*") >= 0 ? [] : contents.split(/\s*\;\s*/) | ||
@@ -808,27 +800,13 @@ // replace content categories with their elements | ||
// This allows AngularJS's ng-* attributes to be allowed, | ||
// customize to fit your needs | ||
attributeWhitelist: [ | ||
/ng\-[a-z\-]+/ | ||
], | ||
// Include any custom element you're using and want to allow | ||
elementWhitelist: [], | ||
isElementValid: function(element) { | ||
return isWhitelistedElement(element) | ||
? true | ||
: elements.indexOf(element) >= 0 | ||
return elements.indexOf(element) >= 0 | ||
}, | ||
isElementObsolete: function(element) { | ||
return isWhitelistedElement(element) | ||
? false | ||
: obsoluteElements.indexOf(element) >= 0 | ||
return obsoluteElements.indexOf(element) >= 0 | ||
}, | ||
isAttributeValidForElement: function(attribute, element) { | ||
if (isGlobalAttribute(attribute) || isWhitelistedAttribute(attribute)) { | ||
return true | ||
} | ||
if (isGlobalAttribute(attribute)) return true | ||
// some elements (like embed) accept any attribute | ||
@@ -841,5 +819,2 @@ // http://drafts.htmlwg.org/html/master/embedded-content-0.html#the-embed-element | ||
isAttributeObsoleteForElement: function(attribute, element) { | ||
// attributes in the whitelist are never considered obsolete | ||
if (isWhitelistedAttribute(attribute)) return false | ||
return obsoleteAttributes.some(function(item) { | ||
@@ -854,5 +829,2 @@ if (item.attribute !== attribute) return false | ||
isAttributeRequiredForElement: function(attribute, element) { | ||
// attributes in the whitelist are never considered required | ||
if (isWhitelistedAttribute(attribute)) return false | ||
return requiredAttributes.some(function(item) { | ||
@@ -859,0 +831,0 @@ return element == item.element && item.attributes.indexOf(attribute) >= 0 |
@@ -0,1 +1,3 @@ | ||
var foundIn = require("../../utils/string-matcher") | ||
module.exports = { | ||
@@ -5,5 +7,9 @@ | ||
func: function(listener, reporter) { | ||
config: { | ||
whitelist: [] | ||
}, | ||
func: function(listener, reporter, config) { | ||
listener.on('attribute', function(name, value) { | ||
if (name.indexOf("on") === 0) { | ||
if (name.indexOf("on") === 0 && !foundIn(name, config.whitelist)) { | ||
reporter.warn( | ||
@@ -10,0 +16,0 @@ "inline-event-handlers", |
@@ -0,1 +1,3 @@ | ||
var foundIn = require("../../utils/string-matcher") | ||
module.exports = { | ||
@@ -5,8 +7,15 @@ | ||
func: function(listener, reporter) { | ||
config: { | ||
whitelist: [] | ||
}, | ||
func: function(listener, reporter, config) { | ||
var elements = [] | ||
listener.on("id", function(name) { | ||
elements.push({id: name, context: this}) | ||
// ignore whitelisted attributes | ||
if (!foundIn(name, config.whitelist)) { | ||
elements.push({id: name, context: this}) | ||
} | ||
}) | ||
@@ -13,0 +22,0 @@ |
@@ -0,1 +1,3 @@ | ||
var foundIn = require("../../utils/string-matcher") | ||
module.exports = { | ||
@@ -5,4 +7,10 @@ | ||
func: function(listener, reporter) { | ||
config: { | ||
whitelist: [ | ||
/ng\-[a-z\-]+/ // AngularJS | ||
] | ||
}, | ||
func: function(listener, reporter, config) { | ||
var validation = this.modules.validation | ||
@@ -12,3 +20,7 @@ | ||
var required = validation.getRequiredAttributesForElement(name) | ||
required.forEach(function(attr) { | ||
// ignore whitelisted attributes | ||
if (foundIn(attr, config.whitelist)) return | ||
if (!this.hasAttribute(attr)) { | ||
@@ -31,2 +43,5 @@ reporter.warn( | ||
// ignore whitelisted attributes | ||
if (foundIn(name, config.whitelist)) return | ||
if (validation.isAttributeObsoleteForElement(name, element)) { | ||
@@ -33,0 +48,0 @@ reporter.warn( |
@@ -5,4 +5,8 @@ module.exports = { | ||
func: function(listener, reporter) { | ||
config: { | ||
whitelist: [] | ||
}, | ||
func: function(listener, reporter, config) { | ||
var validation = this.modules.validation | ||
@@ -19,6 +23,3 @@ , matches = require("dom-utils/src/matches") | ||
listener.on("element", function(name) { | ||
// skip elements without a DOM element for a parent | ||
if (!(this.parentNode && this.parentNode.nodeType == 1)) return | ||
function testGeneralElementLocation(name) { | ||
var child = name | ||
@@ -35,3 +36,3 @@ , parent = this.parentNode.nodeName.toLowerCase() | ||
} | ||
}) | ||
} | ||
@@ -43,6 +44,3 @@ // =========================================================================== | ||
listener.on("element", function(name) { | ||
// don't double warn if the style elements already has a location warning | ||
if (warned.indexOf(this) > -1) return | ||
function testUnscopedStyles(name) { | ||
if (matches(this, "body style:not([scoped])")) { | ||
@@ -62,5 +60,4 @@ reporter.warn( | ||
} | ||
} | ||
}) | ||
// =========================================================================== | ||
@@ -71,6 +68,3 @@ // Make sure <meta> and <link> elements inside <body> have the 'itemprop' | ||
listener.on("element", function(name) { | ||
// don't double warn if the style elements already has a location warning | ||
if (warned.indexOf(this) > -1) return | ||
function testItemProp(name) { | ||
if (matches(this, "body meta:not([itemprop]), body link:not([itemprop])")) { | ||
@@ -84,4 +78,22 @@ reporter.warn( | ||
} | ||
} | ||
listener.on("element", function(name) { | ||
// ignore whitelisted elements | ||
if (matches(this, config.whitelist)) return | ||
// skip elements without a DOM element for a parent | ||
if (!(this.parentNode && this.parentNode.nodeType == 1)) return | ||
// don't double warn if the elements already has a location warning | ||
if (warned.indexOf(this) > -1) return | ||
testGeneralElementLocation.call(this, name) | ||
testUnscopedStyles.call(this, name) | ||
testItemProp.call(this, name) | ||
}) | ||
} | ||
} |
@@ -0,1 +1,3 @@ | ||
var foundIn = require("../../utils/string-matcher") | ||
module.exports = { | ||
@@ -5,7 +7,15 @@ | ||
func: function(listener, reporter) { | ||
config: { | ||
whitelist: [] | ||
}, | ||
func: function(listener, reporter, config) { | ||
var validation = this.modules.validation | ||
listener.on("element", function(name) { | ||
// ignore whitelisted elements | ||
if (foundIn(name, config.whitelist)) return | ||
if (validation.isElementObsolete(name)) { | ||
@@ -12,0 +22,0 @@ reporter.warn( |
@@ -8,5 +8,5 @@ var expect = require("chai").expect | ||
, log | ||
, f1 = function(a, b, c) { log.push({id:"f1", args:[a, b, c], context:this}) } | ||
, f2 = function(a, b, c) { log.push({id:"f2", args:[a, b, c], context:this}) } | ||
, f3 = function(a, b, c) { log.push({id:"f3", args:[a, b, c], context:this}) } | ||
, f1 = function(a, b, c) { log.push({id:"f1", args:[a, b, c], context:this }) } | ||
, f2 = function(a, b, c) { log.push({id:"f2", args:[a, b, c], context:this }) } | ||
, f3 = function(a, b, c) { log.push({id:"f3", args:[a, b, c], context:this }) } | ||
@@ -49,29 +49,32 @@ beforeEach(function() { | ||
it("can invoke the list of callbacks", function() { | ||
var ctx1 = {} | ||
, ctx2 = {} | ||
, ctx3 = {} | ||
cb.fire() | ||
expect(log.length).to.equal(0) | ||
cb.add(f1) | ||
cb.fire("ctx1", ["arg1", "arg2"]) | ||
cb.fire(ctx1, ["arg1", "arg2"]) | ||
expect(log.length).to.equal(1) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", "arg2", undefined], context:"ctx1"}) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", "arg2", undefined], context:ctx1}) | ||
log = [] | ||
cb.add(f2) | ||
cb.fire("ctx1", ["arg1", "arg2", "arg3"]) | ||
cb.fire(ctx1, ["arg1", "arg2", "arg3"]) | ||
expect(log.length).to.equal(2) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", "arg2", "arg3"], context:"ctx1"}) | ||
expect(log[1]).to.deep.equal({id:"f2", args:["arg1", "arg2", "arg3"], context:"ctx1"}) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", "arg2", "arg3"], context:ctx1}) | ||
expect(log[1]).to.deep.equal({id:"f2", args:["arg1", "arg2", "arg3"], context:ctx1}) | ||
log = [] | ||
cb.add(f3) | ||
cb.fire("ctx2") | ||
cb.fire(ctx2) | ||
expect(log.length).to.equal(3) | ||
expect(log[0]).to.deep.equal({id:"f1", args:[undefined, undefined, undefined], context:"ctx2"}) | ||
expect(log[1]).to.deep.equal({id:"f2", args:[undefined, undefined, undefined], context:"ctx2"}) | ||
expect(log[2]).to.deep.equal({id:"f3", args:[undefined, undefined, undefined], context:"ctx2"}) | ||
expect(log[0]).to.deep.equal({id:"f1", args:[undefined, undefined, undefined], context:ctx2}) | ||
expect(log[1]).to.deep.equal({id:"f2", args:[undefined, undefined, undefined], context:ctx2}) | ||
expect(log[2]).to.deep.equal({id:"f3", args:[undefined, undefined, undefined], context:ctx2}) | ||
log = [] | ||
cb.remove(f2) | ||
cb.fire("ctx3", ["arg1"]) | ||
cb.fire(ctx3, ["arg1"]) | ||
expect(log.length).to.equal(2) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", undefined, undefined], context:"ctx3"}) | ||
expect(log[1]).to.deep.equal({id:"f3", args:["arg1", undefined, undefined], context:"ctx3"}) | ||
expect(log[0]).to.deep.equal({id:"f1", args:["arg1", undefined, undefined], context:ctx3}) | ||
expect(log[1]).to.deep.equal({id:"f3", args:["arg1", undefined, undefined], context:ctx3}) | ||
}) | ||
}) | ||
}) |
@@ -487,27 +487,5 @@ describe("HTMLInspector", function() { | ||
it("ignores elements that are whitelisted", function() { | ||
validation.elementWhitelist = validation.elementWhitelist.concat(["foo", "bar", "font", "center"]) | ||
// valid elements | ||
expect(validation.isElementValid("foo")).to.equal(true) | ||
expect(validation.isElementValid("bar")).to.equal(true) | ||
// obsolete elements | ||
expect(validation.isElementObsolete("font")).to.equal(false) | ||
expect(validation.isElementObsolete("center")).to.equal(false) | ||
}) | ||
}) | ||
it("ignores attributes that are whitelisted", function() { | ||
validation.attributeWhitelist = validation.attributeWhitelist.concat(["src", "placeholder", "align", /^bg[a-z]+$/]) | ||
// valid elements | ||
expect(validation.isAttributeValidForElement("placeholder", "select")).to.equal(true) | ||
expect(validation.isAttributeValidForElement("ng-model", "div")).to.equal(true) | ||
// obsolete elements | ||
expect(validation.isAttributeObsoleteForElement("align", "div")).to.equal(false) | ||
expect(validation.isAttributeObsoleteForElement("bgcolor", "body")).to.equal(false) | ||
// required attributes | ||
expect(validation.isAttributeRequiredForElement("src", "img")).to.equal(false) | ||
}) | ||
}) | ||
}) | ||
@@ -764,2 +742,27 @@ describe("Rules", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div id="foobar">' | ||
+ ' <p id="foobar">Foo</p>' | ||
+ ' <p id="barfoo">bar <em id="barfoo">Em</em></p>' | ||
+ '</div>' | ||
) | ||
// whitelist foobar | ||
HTMLInspector.rules.extend("duplicate-ids", { | ||
whitelist: ["foobar"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["duplicate-ids"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(1) | ||
expect(log[0].message).to.equal("The id 'barfoo' appears more than once in the document.") | ||
expect(log[0].context).to.deep.equal([html.querySelector("p#barfoo"), html.querySelector("em#barfoo")]) | ||
}) | ||
}) | ||
@@ -817,2 +820,26 @@ | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div onresize="alert(\'bad!\')">' | ||
+ ' <p>Foo</p>' | ||
+ ' <p>Bar <a href="#" onclick="alert(\'bad!\')">click me</em></p>' | ||
+ '</div>' | ||
) | ||
// whitelist onclick | ||
HTMLInspector.rules.extend("inline-event-handlers", { | ||
whitelist: ["onclick"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["inline-event-handlers"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(1) | ||
expect(log[0].message).to.equal("An 'onresize' attribute was found in the HTML. Use external scripts for event binding instead.") | ||
expect(log[0].context).to.deep.equal(html) | ||
}) | ||
}) | ||
@@ -1387,3 +1414,29 @@ | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div align="right" role="main">' | ||
+ ' <span ng-model="User">Foo</span>' | ||
+ ' <select placeholder="Select a Day">' | ||
+ ' <option>Monday</option>' | ||
+ ' <option>Tuesday</option>' | ||
+ ' </select>' | ||
+ ' <img alt="Image" />' | ||
+ '</div>' | ||
) | ||
HTMLInspector.rules.extend("validate-attributes", function(config) { | ||
config.whitelist.push("src", /place.+/, "align") | ||
return config | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-attributes"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(0) | ||
}) | ||
}) | ||
@@ -1549,2 +1602,40 @@ describe("validate-element-location", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div>' | ||
+ ' <style scoped> .foo { } </style>' | ||
+ ' <h1>This is a <p>Heading!</p> shit</h1>' | ||
+ ' <span>' | ||
+ ' <ul>' | ||
+ ' <li>foo</li>' | ||
+ ' </ul>' | ||
+ ' </span>' | ||
+ ' <ul>' | ||
+ ' <span><li>Foo</li></span>' | ||
+ ' <li>Bar</li>' | ||
+ ' </ul>' | ||
+ ' <p>This is a <title>title</title> element</p>' | ||
+ ' <em><p>emphasize!</p></em>' | ||
+ '</div>' | ||
) | ||
// whitelist foobar | ||
HTMLInspector.rules.extend("validate-element-location", { | ||
whitelist: [":not(p)"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-element-location"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(2) | ||
expect(log[0].message).to.equal("The <p> element cannot be a child of the <h1> element.") | ||
expect(log[0].context).to.equal(html.querySelector("h1 > p")) | ||
expect(log[1].message).to.equal("The <p> element cannot be a child of the <em> element.") | ||
expect(log[1].context).to.equal(html.querySelector("em > p")) | ||
}) | ||
}) | ||
@@ -1658,3 +1749,30 @@ describe("validate-elements", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div>' | ||
+ ' <center>' | ||
+ ' <foo>Foo</foo>' | ||
+ ' <bar>Bar</bar>' | ||
+ ' <font>Font</font>' | ||
+ ' </center>' | ||
+ '</div>' | ||
) | ||
HTMLInspector.rules.extend("validate-elements", function(config) { | ||
config.whitelist.push("foo", "bar", "font", "center") | ||
return config | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-elements"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(0) | ||
}) | ||
}) | ||
}) |
@@ -71,25 +71,2 @@ describe("validation", function() { | ||
it("ignores elements that are whitelisted", function() { | ||
validation.elementWhitelist = validation.elementWhitelist.concat(["foo", "bar", "font", "center"]) | ||
// valid elements | ||
expect(validation.isElementValid("foo")).to.equal(true) | ||
expect(validation.isElementValid("bar")).to.equal(true) | ||
// obsolete elements | ||
expect(validation.isElementObsolete("font")).to.equal(false) | ||
expect(validation.isElementObsolete("center")).to.equal(false) | ||
}) | ||
it("ignores attributes that are whitelisted", function() { | ||
validation.attributeWhitelist = validation.attributeWhitelist.concat(["src", "placeholder", "align", /^bg[a-z]+$/]) | ||
// valid elements | ||
expect(validation.isAttributeValidForElement("placeholder", "select")).to.equal(true) | ||
expect(validation.isAttributeValidForElement("ng-model", "div")).to.equal(true) | ||
// obsolete elements | ||
expect(validation.isAttributeObsoleteForElement("align", "div")).to.equal(false) | ||
expect(validation.isAttributeObsoleteForElement("bgcolor", "body")).to.equal(false) | ||
// required attributes | ||
expect(validation.isAttributeRequiredForElement("src", "img")).to.equal(false) | ||
}) | ||
}) | ||
}) |
@@ -51,2 +51,27 @@ describe("duplicate-ids", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div id="foobar">' | ||
+ ' <p id="foobar">Foo</p>' | ||
+ ' <p id="barfoo">bar <em id="barfoo">Em</em></p>' | ||
+ '</div>' | ||
) | ||
// whitelist foobar | ||
HTMLInspector.rules.extend("duplicate-ids", { | ||
whitelist: ["foobar"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["duplicate-ids"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(1) | ||
expect(log[0].message).to.equal("The id 'barfoo' appears more than once in the document.") | ||
expect(log[0].context).to.deep.equal([html.querySelector("p#barfoo"), html.querySelector("em#barfoo")]) | ||
}) | ||
}) |
@@ -51,2 +51,26 @@ describe("inline-event-handlers", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div onresize="alert(\'bad!\')">' | ||
+ ' <p>Foo</p>' | ||
+ ' <p>Bar <a href="#" onclick="alert(\'bad!\')">click me</em></p>' | ||
+ '</div>' | ||
) | ||
// whitelist onclick | ||
HTMLInspector.rules.extend("inline-event-handlers", { | ||
whitelist: ["onclick"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["inline-event-handlers"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(1) | ||
expect(log[0].message).to.equal("An 'onresize' attribute was found in the HTML. Use external scripts for event binding instead.") | ||
expect(log[0].context).to.deep.equal(html) | ||
}) | ||
}) |
@@ -167,3 +167,29 @@ describe("validate-attributes", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div align="right" role="main">' | ||
+ ' <span ng-model="User">Foo</span>' | ||
+ ' <select placeholder="Select a Day">' | ||
+ ' <option>Monday</option>' | ||
+ ' <option>Tuesday</option>' | ||
+ ' </select>' | ||
+ ' <img alt="Image" />' | ||
+ '</div>' | ||
) | ||
HTMLInspector.rules.extend("validate-attributes", function(config) { | ||
config.whitelist.push("src", /place.+/, "align") | ||
return config | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-attributes"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(0) | ||
}) | ||
}) |
@@ -160,2 +160,40 @@ describe("validate-element-location", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div>' | ||
+ ' <style scoped> .foo { } </style>' | ||
+ ' <h1>This is a <p>Heading!</p> shit</h1>' | ||
+ ' <span>' | ||
+ ' <ul>' | ||
+ ' <li>foo</li>' | ||
+ ' </ul>' | ||
+ ' </span>' | ||
+ ' <ul>' | ||
+ ' <span><li>Foo</li></span>' | ||
+ ' <li>Bar</li>' | ||
+ ' </ul>' | ||
+ ' <p>This is a <title>title</title> element</p>' | ||
+ ' <em><p>emphasize!</p></em>' | ||
+ '</div>' | ||
) | ||
// whitelist foobar | ||
HTMLInspector.rules.extend("validate-element-location", { | ||
whitelist: [":not(p)"] | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-element-location"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(2) | ||
expect(log[0].message).to.equal("The <p> element cannot be a child of the <h1> element.") | ||
expect(log[0].context).to.equal(html.querySelector("h1 > p")) | ||
expect(log[1].message).to.equal("The <p> element cannot be a child of the <em> element.") | ||
expect(log[1].context).to.equal(html.querySelector("em > p")) | ||
}) | ||
}) |
@@ -108,2 +108,29 @@ describe("validate-elements", function() { | ||
it("allows for customization by altering the config object", function() { | ||
var html = parseHTML('' | ||
+ '<div>' | ||
+ ' <center>' | ||
+ ' <foo>Foo</foo>' | ||
+ ' <bar>Bar</bar>' | ||
+ ' <font>Font</font>' | ||
+ ' </center>' | ||
+ '</div>' | ||
) | ||
HTMLInspector.rules.extend("validate-elements", function(config) { | ||
config.whitelist.push("foo", "bar", "font", "center") | ||
return config | ||
}) | ||
HTMLInspector.inspect({ | ||
useRules: ["validate-elements"], | ||
domRoot: html, | ||
onComplete: onComplete | ||
}) | ||
expect(log.length).to.equal(0) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
360348
62
7045
377