/**
* @fileoverview Control elements must be associated with a text label
* @author jessebeach
*/
// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------
import { RuleTester } from 'eslint';
import { configs } from '../../../src/index';
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
import parsers from '../../__util__/helpers/parsers';
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
import rule from '../../../src/rules/control-has-associated-label';
// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------
const ruleTester = new RuleTester();
const ruleName = 'jsx-a11y/control-has-associated-label';
const expectedError = {
message: 'A control must be associated with a text label.',
type: 'JSXOpeningElement',
};
const alwaysValid = [
// Custom Control Components
{ code: 'Save', options: [{ depth: 3, controlComponents: ['CustomControl'] }] },
{ code: '', options: [{ depth: 3, controlComponents: ['CustomControl'], labelAttributes: ['label'] }] },
{ code: 'Save', settings: { 'jsx-a11y': { components: { CustomControl: 'button' } } } },
// Interactive Elements
{ code: '' },
{ code: '' },
{ code: '', options: [{ depth: 3 }] },
{ code: '', options: [{ depth: 9 }] },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '', options: [{ depth: 3, labelAttributes: ['label'] }] },
{ code: 'Save' },
{ code: 'Save' },
{ code: 'Save' },
{ code: '' },
{ code: '' },
{ code: '
Save | ' },
// Interactive Roles
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: 'Save
' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
// Non-interactive Elements
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '
' },
{ code: '' },
{ code: '' },
{ code: ' ' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '
' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '
' },
{ code: '' },
{ code: '
' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '
' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
// Non-interactive Roles
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
// Via config
// Inputs. Ignore them because they might get a label from a wrapping label element.
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
// Marginal interactive elements. It is difficult to insist that these
// elements contain a text label.
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '
' },
{ code: '' },
// Interactive roles to ignore
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
{ code: '' },
];
const neverValid = [
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', options: [{ depth: 3 }], errors: [expectedError] },
{ code: '', options: [{ depth: 3, controlComponents: ['CustomControl'] }], errors: [expectedError] },
{ code: '', errors: [expectedError], settings: { 'jsx-a11y': { components: { CustomControl: 'button' } } } },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: ' | ', errors: [expectedError] },
{ code: ' | ', errors: [expectedError] },
// Interactive Roles
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
{ code: '', errors: [expectedError] },
];
const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
ruleTester.run(`${ruleName}:recommended`, rule, {
valid: parsers.all([].concat(
...alwaysValid,
))
.map(ruleOptionsMapperFactory(recommendedOptions))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
))
.map(ruleOptionsMapperFactory(recommendedOptions))
.map(parserOptionsMapper),
});
const strictOptions = (configs.strict.rules[ruleName][1] || {});
ruleTester.run(`${ruleName}:strict`, rule, {
valid: parsers.all([].concat(
...alwaysValid,
))
.map(ruleOptionsMapperFactory(strictOptions))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
...neverValid,
))
.map(ruleOptionsMapperFactory(strictOptions))
.map(parserOptionsMapper),
});
ruleTester.run(`${ruleName}:no-config`, rule, {
valid: parsers.all([].concat(
{ code: '' },
{ code: '' },
))
.map(parserOptionsMapper),
invalid: parsers.all([].concat(
{ code: '', errors: [expectedError] },
))
.map(parserOptionsMapper),
});