/**
* @fileoverview Enforce label tags have an associated control.
* @author Jesse Beach
*/
// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------
import { RuleTester } from 'eslint';
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
import parsers from '../../__util__/helpers/parsers';
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: '', options: [{ depth: 4 }] },
{ code: '' },
{ code: '' },
{ code: '
' },
// Custom label component.
{ code: '', options: [{ labelComponents: ['CustomLabel'] }] },
{ code: '', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }] },
{ code: '', settings: componentsSettings },
// Custom label attributes.
{ code: '', options: [{ labelAttributes: ['label'] }] },
// Glob support for controlComponents option.
{ code: '', options: [{ controlComponents: ['Custom*'] }] },
{ code: '', options: [{ controlComponents: ['*Label'] }] },
];
const nestingValid = [
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '', options: [{ depth: 3 }] },
{ code: '', options: [{ depth: 4 }] },
{ code: '', options: [{ depth: 5 }] },
{ code: '', options: [{ depth: 5 }] },
{ code: '', options: [{ depth: 5 }] },
// Other controls
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
// Custom controlComponents.
{ code: '', options: [{ controlComponents: ['CustomInput'] }] },
{ code: '', settings: componentsSettings },
{ code: 'A label', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }] },
{ code: '', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }] },
// Glob support for controlComponents option.
{ code: '', options: [{ controlComponents: ['Custom*'] }] },
{ code: '', options: [{ controlComponents: ['*Input'] }] },
];
const bothValid = [
{ code: '', options: [{ depth: 4 }] },
{ code: '' },
{ code: '' },
{ code: '' },
// Custom label component.
{ code: '', options: [{ labelComponents: ['CustomLabel'] }] },
{ code: '', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }] },
{ code: '', settings: componentsSettings },
{ code: '', settings: componentsSettings },
// Custom label attributes.
{ code: '', options: [{ labelAttributes: ['label'] }] },
{ code: '' },
];
const alwaysValid = [
{ code: '' },
{ code: '' },
{ code: '' },
];
const htmlForInvalid = [
{ code: '', options: [{ depth: 4 }], errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
// Custom label component.
{ code: '', options: [{ labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', settings: componentsSettings, errors: [expectedError] },
// Custom label attributes.
{ code: '', options: [{ labelAttributes: ['label'] }], errors: [expectedError] },
];
const nestingInvalid = [
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', options: [{ depth: 3 }], errors: [expectedError] },
{ code: '', options: [{ depth: 4 }], errors: [expectedError] },
{ code: '', options: [{ depth: 5 }], errors: [expectedError] },
{ code: '', options: [{ depth: 5 }], errors: [expectedError] },
{ code: '', options: [{ depth: 5 }], errors: [expectedError] },
// Custom controlComponents.
{ code: '', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedError] },
{ code: 'A label', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedError] },
{ code: '', settings: componentsSettings, errors: [expectedError] },
{ code: 'A label', settings: componentsSettings, errors: [expectedError] },
];
const neverValid = [
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
// Custom label component.
{ code: '', options: [{ labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', settings: componentsSettings, errors: [expectedError] },
// Custom label attributes.
{ code: '', options: [{ labelAttributes: ['label'] }], errors: [expectedError] },
// Custom controlComponents.
{ code: '', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedError] },
{ code: '', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedError] },
{ code: '', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedError] },
{ code: '', settings: componentsSettings, errors: [expectedError] },
{ code: '', settings: componentsSettings, errors: [expectedError] },
];
// htmlFor valid
ruleTester.run(ruleName, rule, {
valid: parsers.all([].concat(
...alwaysValid,
...htmlForValid,
))
.map(ruleOptionsMapperFactory({
assert: 'htmlFor',
}))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
...nestingInvalid,
))
.map(ruleOptionsMapperFactory({
assert: 'htmlFor',
}))
.map(parserOptionsMapper),
});
// nesting valid
ruleTester.run(ruleName, rule, {
valid: parsers.all([].concat(
...alwaysValid,
...nestingValid,
))
.map(ruleOptionsMapperFactory({
assert: 'nesting',
}))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
...htmlForInvalid,
))
.map(ruleOptionsMapperFactory({
assert: 'nesting',
}))
.map(parserOptionsMapper),
});
// either valid
ruleTester.run(ruleName, rule, {
valid: parsers.all([].concat(
...alwaysValid,
...htmlForValid,
...nestingValid,
))
.map(ruleOptionsMapperFactory({
assert: 'either',
}))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
)).map(parserOptionsMapper),
});
// both valid
ruleTester.run(ruleName, rule, {
valid: parsers.all([].concat(
...alwaysValid,
...bothValid,
))
.map(ruleOptionsMapperFactory({
assert: 'both',
}))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
)).map(parserOptionsMapper),
});