Skip to content

Commit 3db3dcc

Browse files
oloreChristopher Quadflieg
authored and
Christopher Quadflieg
committed
feat: new rule: input-requires-label - All inputs require a label (#159)
1 parent 33de7ff commit 3db3dcc

File tree

3 files changed

+115
-113
lines changed

3 files changed

+115
-113
lines changed

src/rules/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export { default as idClassValue } from './id-class-value';
1616
export { default as idUnique } from './id-unique';
1717
export { default as inlineScriptDisabled } from './inline-script-disabled';
1818
export { default as inlineStyleDisabled } from './inline-style-disabled';
19+
export { default as inputRequiresLabel } from './input-requires-label';
1920
export { default as scriptDisabled } from './script-disabled';
2021
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled';
2122
export { default as specCharEscape } from './spec-char-escape';

src/rules/input-requires-label.js

+43-46
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,46 @@
11
export default {
2-
id: 'input-requires-label',
3-
description: 'All [ input ] tags must have a corresponding [ label ] tag. ',
4-
init: function (parser, reporter) {
5-
var self = this
6-
var labelTags = []
7-
var inputTags = []
8-
9-
parser.addListener('tagstart', function (event) {
10-
var tagName = event.tagName.toLowerCase()
11-
var mapAttrs = parser.getMapAttrs(event.attrs)
12-
var col = event.col + tagName.length + 1
13-
14-
if (tagName === 'input') {
15-
inputTags.push({ event: event, col: col, id: mapAttrs['id'] })
16-
}
17-
18-
if (tagName === 'label') {
19-
if ('for' in mapAttrs && mapAttrs['for'] !== '') {
20-
labelTags.push({ event: event, col: col, forValue: mapAttrs['for'] })
21-
}
22-
}
23-
})
24-
25-
parser.addListener('end', function () {
26-
inputTags.forEach(function (inputTag) {
27-
if (!hasMatchingLabelTag(inputTag)) {
28-
reporter.warn(
29-
'No matching [ label ] tag found.',
30-
inputTag.event.line,
31-
inputTag.col,
32-
self,
33-
inputTag.event.raw
34-
)
35-
}
36-
})
37-
})
38-
39-
function hasMatchingLabelTag(inputTag) {
40-
var found = false
41-
labelTags.forEach(function (labelTag) {
42-
if (inputTag.id && inputTag.id === labelTag.forValue) {
43-
found = true
2+
id: 'input-requires-label',
3+
description: 'All [ input ] tags must have a corresponding [ label ] tag. ',
4+
init: function(parser, reporter){
5+
var self = this,
6+
labelTags = [],
7+
inputTags = [];
8+
9+
parser.addListener('tagstart', function(event) {
10+
var tagName = event.tagName.toLowerCase(),
11+
mapAttrs = parser.getMapAttrs(event.attrs),
12+
col = event.col + tagName.length + 1;
13+
14+
if (tagName === 'input') {
15+
inputTags.push({event: event, col: col, id: mapAttrs['id']});
16+
}
17+
18+
if (tagName === 'label') {
19+
if (('for' in mapAttrs) && mapAttrs['for'] !== '') {
20+
labelTags.push({event: event, col: col, forValue: mapAttrs['for']});
21+
}
22+
}
23+
24+
});
25+
26+
parser.addListener('end', function() {
27+
inputTags.forEach(function(inputTag) {
28+
if (!hasMatchingLabelTag(inputTag)) {
29+
reporter.warn('No matching [ label ] tag found.', inputTag.event.line, inputTag.col, self, inputTag.event.raw);
30+
}
31+
});
32+
});
33+
34+
35+
function hasMatchingLabelTag(inputTag) {
36+
var found = false;
37+
labelTags.forEach(function(labelTag){
38+
if (inputTag.id && (inputTag.id === labelTag.forValue)) {
39+
found = true;
40+
}
41+
});
42+
return found;
43+
4444
}
45-
})
46-
return found
4745
}
48-
},
49-
}
46+
};
+71-67
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,71 @@
1-
const expect = require('expect.js')
2-
3-
const HTMLHint = require('../../dist/htmlhint.js').HTMLHint
4-
5-
const ruleId = 'input-requires-label'
6-
const ruleOptions = {}
7-
8-
ruleOptions[ruleId] = true
9-
10-
describe(`Rules: ${ruleId}`, function () {
11-
describe('Successful cases', function () {
12-
it('Input tag with a matching label before should result in no error', function () {
13-
var code = '<label for="some-id"/><input id="some-id" type="password" />'
14-
var messages = HTMLHint.verify(code, ruleOptions)
15-
expect(messages.length).to.be(0)
16-
})
17-
18-
it('Input tag with a matching label after should result in no error', function () {
19-
var code = '<input id="some-id" type="password" /> <label for="some-id"/>'
20-
var messages = HTMLHint.verify(code, ruleOptions)
21-
expect(messages.length).to.be(0)
22-
})
23-
})
24-
25-
describe('Error cases', function () {
26-
it('Input tag with no matching label should result in an error', function () {
27-
var code = '<input type="password">'
28-
var messages = HTMLHint.verify(code, ruleOptions)
29-
expect(messages.length).to.be(1)
30-
expect(messages[0].rule.id).to.be(ruleId)
31-
expect(messages[0].line).to.be(1)
32-
expect(messages[0].col).to.be(7)
33-
expect(messages[0].type).to.be('warning')
34-
})
35-
36-
it("Input tag with label that doesn't match id should result in error", function () {
37-
var code =
38-
'<input id="some-id" type="password" /> <label for="some-other-id"/>'
39-
var messages = HTMLHint.verify(code, ruleOptions)
40-
expect(messages.length).to.be(1)
41-
expect(messages[0].rule.id).to.be(ruleId)
42-
expect(messages[0].line).to.be(1)
43-
expect(messages[0].col).to.be(7)
44-
expect(messages[0].type).to.be('warning')
45-
})
46-
47-
it('Input tag with blank label:for should result in error', function () {
48-
var code = '<input id="some-id" type="password" /> <label for=""/>'
49-
var messages = HTMLHint.verify(code, ruleOptions)
50-
expect(messages.length).to.be(1)
51-
expect(messages[0].rule.id).to.be(ruleId)
52-
expect(messages[0].line).to.be(1)
53-
expect(messages[0].col).to.be(7)
54-
expect(messages[0].type).to.be('warning')
55-
})
56-
57-
it('Input tag with no id should result in error', function () {
58-
var code = '<input type="password" /> <label for="something"/>'
59-
var messages = HTMLHint.verify(code, ruleOptions)
60-
expect(messages.length).to.be(1)
61-
expect(messages[0].rule.id).to.be(ruleId)
62-
expect(messages[0].line).to.be(1)
63-
expect(messages[0].col).to.be(7)
64-
expect(messages[0].type).to.be('warning')
65-
})
66-
})
67-
})
1+
const expect = require("expect.js");
2+
3+
const HTMLHint = require('../../dist/htmlhint.js').HTMLHint;
4+
5+
const ruleId = 'input-requires-label';
6+
const ruleOptions = {};
7+
8+
ruleOptions[ruleId] = true;
9+
10+
describe(`Rules: ${ruleId}`, function(){
11+
12+
describe('Successful cases', function() {
13+
14+
it('Input tag with a matching label before should result in no error', function () {
15+
var code = '<label for="some-id"/><input id="some-id" type="password" />';
16+
var messages = HTMLHint.verify(code, ruleOptions);
17+
expect(messages.length).to.be(0);
18+
});
19+
20+
it('Input tag with a matching label after should result in no error', function () {
21+
var code = '<input id="some-id" type="password" /> <label for="some-id"/>';
22+
var messages = HTMLHint.verify(code, ruleOptions);
23+
expect(messages.length).to.be(0);
24+
});
25+
});
26+
27+
28+
describe('Error cases', function() {
29+
30+
it('Input tag with no matching label should result in an error', function () {
31+
var code = '<input type="password">';
32+
var messages = HTMLHint.verify(code, ruleOptions);
33+
expect(messages.length).to.be(1);
34+
expect(messages[0].rule.id).to.be(ruleId);
35+
expect(messages[0].line).to.be(1);
36+
expect(messages[0].col).to.be(7);
37+
expect(messages[0].type).to.be('warning');
38+
});
39+
40+
it('Input tag with label that doesn\'t match id should result in error', function () {
41+
var code = '<input id="some-id" type="password" /> <label for="some-other-id"/>';
42+
var messages = HTMLHint.verify(code, ruleOptions);
43+
expect(messages.length).to.be(1);
44+
expect(messages[0].rule.id).to.be(ruleId);
45+
expect(messages[0].line).to.be(1);
46+
expect(messages[0].col).to.be(7);
47+
expect(messages[0].type).to.be('warning');
48+
});
49+
50+
it('Input tag with blank label:for should result in error', function () {
51+
var code = '<input id="some-id" type="password" /> <label for=""/>';
52+
var messages = HTMLHint.verify(code, ruleOptions);
53+
expect(messages.length).to.be(1);
54+
expect(messages[0].rule.id).to.be(ruleId);
55+
expect(messages[0].line).to.be(1);
56+
expect(messages[0].col).to.be(7);
57+
expect(messages[0].type).to.be('warning');
58+
});
59+
60+
it('Input tag with no id should result in error', function () {
61+
var code = '<input type="password" /> <label for="something"/>';
62+
var messages = HTMLHint.verify(code, ruleOptions);
63+
expect(messages.length).to.be(1);
64+
expect(messages[0].rule.id).to.be(ruleId);
65+
expect(messages[0].line).to.be(1);
66+
expect(messages[0].col).to.be(7);
67+
expect(messages[0].type).to.be('warning');
68+
});
69+
});
70+
71+
});

0 commit comments

Comments
 (0)