Permalink
Cannot retrieve contributors at this time
219 lines (204 sloc)
11.4 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/label-has-associated-control-test.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview Enforce label tags have an associated control. | |
* @author Jesse Beach | |
*/ | |
// ----------------------------------------------------------------------------- | |
// Requirements | |
// ----------------------------------------------------------------------------- | |
import { RuleTester } from 'eslint'; | |
import parserOptionsMapper from '../../__util__/parserOptionsMapper'; | |
import rule from '../../../src/rules/label-has-associated-control'; | |
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory'; | |
// ----------------------------------------------------------------------------- | |
// Tests | |
// ----------------------------------------------------------------------------- | |
const ruleTester = new RuleTester(); | |
const ruleName = 'label-has-associated-control'; | |
const expectedError = { | |
message: 'A form label must be associated with a control.', | |
type: 'JSXOpeningElement', | |
}; | |
const componentsSettings = { | |
'jsx-a11y': { | |
components: { | |
CustomInput: 'input', | |
CustomLabel: 'label', | |
}, | |
}, | |
}; | |
const htmlForValid = [ | |
{ code: '<label htmlFor="js_id"><span><span><span>A label</span></span></span></label>', options: [{ depth: 4 }] }, | |
{ code: '<label htmlFor="js_id" aria-label="A label" />' }, | |
{ code: '<label htmlFor="js_id" aria-labelledby="A label" />' }, | |
{ code: '<div><label htmlFor="js_id">A label</label><input id="js_id" /></div>' }, | |
// Custom label component. | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ labelComponents: ['CustomLabel'] }] }, | |
{ code: '<CustomLabel htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }] }, | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', settings: componentsSettings }, | |
// Custom label attributes. | |
{ code: '<label htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'] }] }, | |
// Glob support for controlComponents option. | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['Custom*'] }] }, | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['*Label'] }] }, | |
]; | |
const nestingValid = [ | |
{ code: '<label>A label<input /></label>' }, | |
{ code: '<label>A label<textarea /></label>' }, | |
{ code: '<label><img alt="A label" /><input /></label>' }, | |
{ code: '<label><img aria-label="A label" /><input /></label>' }, | |
{ code: '<label><span>A label<input /></span></label>' }, | |
{ code: '<label><span><span>A label<input /></span></span></label>', options: [{ depth: 3 }] }, | |
{ code: '<label><span><span><span>A label<input /></span></span></span></label>', options: [{ depth: 4 }] }, | |
{ code: '<label><span><span><span><span>A label</span><input /></span></span></span></label>', options: [{ depth: 5 }] }, | |
{ code: '<label><span><span><span><span aria-label="A label" /><input /></span></span></span></label>', options: [{ depth: 5 }] }, | |
{ code: '<label><span><span><span><input aria-label="A label" /></span></span></span></label>', options: [{ depth: 5 }] }, | |
// Other controls | |
{ code: '<label>foo<meter /></label>' }, | |
{ code: '<label>foo<output /></label>' }, | |
{ code: '<label>foo<progress /></label>' }, | |
{ code: '<label>foo<textarea /></label>' }, | |
// Custom controlComponents. | |
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }] }, | |
{ code: '<label><span>A label<CustomInput /></span></label>', settings: componentsSettings }, | |
{ code: '<CustomLabel><span>A label<CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }] }, | |
{ code: '<CustomLabel><span label="A label"><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }] }, | |
// Glob support for controlComponents option. | |
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['Custom*'] }] }, | |
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['*Input'] }] }, | |
]; | |
const bothValid = [ | |
{ code: '<label htmlFor="js_id"><span><span><span>A label<input /></span></span></span></label>', options: [{ depth: 4 }] }, | |
{ code: '<label htmlFor="js_id" aria-label="A label"><input /></label>' }, | |
{ code: '<label htmlFor="js_id" aria-labelledby="A label"><input /></label>' }, | |
{ code: '<label htmlFor="js_id" aria-labelledby="A label"><textarea /></label>' }, | |
// Custom label component. | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label"><input /></CustomLabel>', options: [{ labelComponents: ['CustomLabel'] }] }, | |
{ code: '<CustomLabel htmlFor="js_id" label="A label"><input /></CustomLabel>', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }] }, | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label"><input /></CustomLabel>', settings: componentsSettings }, | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label"><CustomInput /></CustomLabel>', settings: componentsSettings }, | |
// Custom label attributes. | |
{ code: '<label htmlFor="js_id" label="A label"><input /></label>', options: [{ labelAttributes: ['label'] }] }, | |
{ code: '<label htmlFor="selectInput">Some text<select id="selectInput" /></label>' }, | |
]; | |
const alwaysValid = [ | |
{ code: '<div />' }, | |
{ code: '<CustomElement />' }, | |
{ code: '<input type="hidden" />' }, | |
]; | |
const htmlForInvalid = [ | |
{ code: '<label htmlFor="js_id"><span><span><span>A label</span></span></span></label>', options: [{ depth: 4 }], errors: [expectedError] }, | |
{ code: '<label htmlFor="js_id" aria-label="A label" />', errors: [expectedError] }, | |
{ code: '<label htmlFor="js_id" aria-labelledby="A label" />', errors: [expectedError] }, | |
// Custom label component. | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', settings: componentsSettings, errors: [expectedError] }, | |
// Custom label attributes. | |
{ code: '<label htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'] }], errors: [expectedError] }, | |
]; | |
const nestingInvalid = [ | |
{ code: '<label>A label<input /></label>', errors: [expectedError] }, | |
{ code: '<label>A label<textarea /></label>', errors: [expectedError] }, | |
{ code: '<label><img alt="A label" /><input /></label>', errors: [expectedError] }, | |
{ code: '<label><img aria-label="A label" /><input /></label>', errors: [expectedError] }, | |
{ code: '<label><span>A label<input /></span></label>', errors: [expectedError] }, | |
{ code: '<label><span><span>A label<input /></span></span></label>', options: [{ depth: 3 }], errors: [expectedError] }, | |
{ code: '<label><span><span><span>A label<input /></span></span></span></label>', options: [{ depth: 4 }], errors: [expectedError] }, | |
{ code: '<label><span><span><span><span>A label</span><input /></span></span></span></label>', options: [{ depth: 5 }], errors: [expectedError] }, | |
{ code: '<label><span><span><span><span aria-label="A label" /><input /></span></span></span></label>', options: [{ depth: 5 }], errors: [expectedError] }, | |
{ code: '<label><span><span><span><input aria-label="A label" /></span></span></span></label>', options: [{ depth: 5 }], errors: [expectedError] }, | |
// Custom controlComponents. | |
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel><span>A label<CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel><span label="A label"><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedError] }, | |
{ code: '<label><span>A label<CustomInput /></span></label>', settings: componentsSettings, errors: [expectedError] }, | |
{ code: '<CustomLabel><span>A label<CustomInput /></span></CustomLabel>', settings: componentsSettings, errors: [expectedError] }, | |
]; | |
const neverValid = [ | |
{ code: '<label htmlFor="js_id" />', errors: [expectedError] }, | |
{ code: '<label htmlFor="js_id"><input /></label>', errors: [expectedError] }, | |
{ code: '<label htmlFor="js_id"><textarea /></label>', errors: [expectedError] }, | |
{ code: '<label></label>', errors: [expectedError] }, | |
{ code: '<label>A label</label>', errors: [expectedError] }, | |
{ code: '<div><label /><input /></div>', errors: [expectedError] }, | |
{ code: '<div><label>A label</label><input /></div>', errors: [expectedError] }, | |
// Custom label component. | |
{ code: '<CustomLabel aria-label="A label" />', options: [{ labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel label="A label" />', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel aria-label="A label" />', settings: componentsSettings, errors: [expectedError] }, | |
// Custom label attributes. | |
{ code: '<label label="A label" />', options: [{ labelAttributes: ['label'] }], errors: [expectedError] }, | |
// Custom controlComponents. | |
{ code: '<label><span><CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedError] }, | |
{ code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedError] }, | |
{ code: '<label><span><CustomInput /></span></label>', settings: componentsSettings, errors: [expectedError] }, | |
{ code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', settings: componentsSettings, errors: [expectedError] }, | |
]; | |
// htmlFor valid | |
ruleTester.run(ruleName, rule, { | |
valid: [ | |
...alwaysValid, | |
...htmlForValid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'htmlFor', | |
})) | |
.map(parserOptionsMapper), | |
invalid: [ | |
...neverValid, | |
...nestingInvalid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'htmlFor', | |
})) | |
.map(parserOptionsMapper), | |
}); | |
// nesting valid | |
ruleTester.run(ruleName, rule, { | |
valid: [ | |
...alwaysValid, | |
...nestingValid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'nesting', | |
})) | |
.map(parserOptionsMapper), | |
invalid: [ | |
...neverValid, | |
...htmlForInvalid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'nesting', | |
})) | |
.map(parserOptionsMapper), | |
}); | |
// either valid | |
ruleTester.run(ruleName, rule, { | |
valid: [ | |
...alwaysValid, | |
...htmlForValid, | |
...nestingValid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'either', | |
})) | |
.map(parserOptionsMapper), | |
invalid: [ | |
...neverValid, | |
].map(parserOptionsMapper), | |
}); | |
// both valid | |
ruleTester.run(ruleName, rule, { | |
valid: [ | |
...alwaysValid, | |
...bothValid, | |
] | |
.map(ruleOptionsMapperFactory({ | |
assert: 'both', | |
})) | |
.map(parserOptionsMapper), | |
invalid: [ | |
...neverValid, | |
].map(parserOptionsMapper), | |
}); |