mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-02 06:02:19 +00:00
0.2.0 - Mid migration
This commit is contained in:
parent
139e6a915e
commit
7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions
51
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/accessible-emoji-test.js
generated
vendored
Normal file
51
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/accessible-emoji-test.js
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce <marquee> elements are not used.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/accessible-emoji';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Emojis should be wrapped in <span>, have role="img", and have an accessible description with aria-label or aria-labelledby.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('accessible-emoji', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<span />' },
|
||||
{ code: '<span>No emoji here!</span>' },
|
||||
{ code: '<span role="img" aria-label="Panda face">🐼</span>' },
|
||||
{ code: '<span role="img" aria-label="Snowman">☃</span>' },
|
||||
{ code: '<span role="img" aria-labelledby="id1">🐼</span>' },
|
||||
{ code: '<span role="img" aria-labelledby="id1">☃</span>' },
|
||||
{ code: '<span role="img" aria-labelledby="id1" aria-label="Snowman">☃</span>' },
|
||||
{ code: '<span>{props.emoji}</span>' },
|
||||
{ code: '<span aria-hidden>{props.emoji}</span>' },
|
||||
{ code: '<span aria-hidden="true">🐼</span>' },
|
||||
{ code: '<span aria-hidden>🐼</span>' },
|
||||
{ code: '<div aria-hidden="true">🐼</div>' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<span>🐼</span>', errors: [expectedError] },
|
||||
{ code: '<span>foo🐼bar</span>', errors: [expectedError] },
|
||||
{ code: '<span>foo 🐼 bar</span>', errors: [expectedError] },
|
||||
{ code: '<i role="img" aria-label="Panda face">🐼</i>', errors: [expectedError] },
|
||||
{ code: '<i role="img" aria-labelledby="id1">🐼</i>', errors: [expectedError] },
|
||||
{ code: '<Foo>🐼</Foo>', errors: [expectedError] },
|
||||
{ code: '<span aria-hidden="false">🐼</span>', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
268
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/alt-text-test.js
generated
vendored
Normal file
268
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/alt-text-test.js
generated
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce all elements that require alternative text have it.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/alt-text';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const missingPropError = (type) => ({
|
||||
message: `${type} elements must have an alt prop, either with meaningful text, or an empty string for decorative images.`,
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const altValueError = (type) => ({
|
||||
message: `Invalid alt value for ${type}. \
|
||||
Use alt="" for presentational images.`,
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const ariaLabelValueError = 'The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images.';
|
||||
const ariaLabelledbyValueError = 'The aria-labelledby attribute must have a value. The alt attribute is preferred over aria-labelledby for images.';
|
||||
|
||||
const preferAltError = () => ({
|
||||
message: 'Prefer alt="" over a presentational role. First rule of aria is to not use aria if it can be achieved via native HTML.',
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const objectError = 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props.';
|
||||
|
||||
const areaError = 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.';
|
||||
|
||||
const inputImageError = '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.';
|
||||
|
||||
const array = [{
|
||||
img: ['Thumbnail', 'Image'],
|
||||
object: ['Object'],
|
||||
area: ['Area'],
|
||||
'input[type="image"]': ['InputImage'],
|
||||
}];
|
||||
|
||||
ruleTester.run('alt-text', rule, {
|
||||
valid: [
|
||||
// DEFAULT ELEMENT 'img' TESTS
|
||||
{ code: '<img alt="foo" />;' },
|
||||
{ code: '<img alt={"foo"} />;' },
|
||||
{ code: '<img alt={alt} />;' },
|
||||
{ code: '<img ALT="foo" />;' },
|
||||
{ code: '<img ALT={`This is the ${alt} text`} />;' },
|
||||
{ code: '<img ALt="foo" />;' },
|
||||
{ code: '<img alt="foo" salt={undefined} />;' },
|
||||
{ code: '<img {...this.props} alt="foo" />' },
|
||||
{ code: '<a />' },
|
||||
{ code: '<div />' },
|
||||
{ code: '<img alt={function(e) {} } />' },
|
||||
{ code: '<div alt={function(e) {} } />' },
|
||||
{ code: '<img alt={() => void 0} />' },
|
||||
{ code: '<IMG />' },
|
||||
{ code: '<UX.Layout>test</UX.Layout>' },
|
||||
{ code: '<img alt={alt || "Alt text" } />' },
|
||||
{ code: '<img alt={photo.caption} />;' },
|
||||
{ code: '<img alt={bar()} />;' },
|
||||
{ code: '<img alt={foo.bar || ""} />' },
|
||||
{ code: '<img alt={bar() || ""} />' },
|
||||
{ code: '<img alt={foo.bar() || ""} />' },
|
||||
{ code: '<img alt="" />' },
|
||||
{ code: '<img alt={`${undefined}`} />' },
|
||||
{ code: '<img alt=" " />' },
|
||||
{ code: '<img alt="" role="presentation" />' },
|
||||
{ code: '<img alt="" role="none" />' },
|
||||
{ code: '<img alt="" role={`presentation`} />' },
|
||||
{ code: '<img alt="" role={"presentation"} />' },
|
||||
{ code: '<img alt="this is lit..." role="presentation" />' },
|
||||
{ code: '<img alt={error ? "not working": "working"} />' },
|
||||
{ code: '<img alt={undefined ? "working": "not working"} />' },
|
||||
{ code: '<img alt={plugin.name + " Logo"} />' },
|
||||
{ code: '<img aria-label="foo" />' },
|
||||
{ code: '<img aria-labelledby="id1" />' },
|
||||
|
||||
// DEFAULT <object> TESTS
|
||||
{ code: '<object aria-label="foo" />' },
|
||||
{ code: '<object aria-labelledby="id1" />' },
|
||||
{ code: '<object>Foo</object>' },
|
||||
{ code: '<object><p>This is descriptive!</p></object>' },
|
||||
{ code: '<Object />' },
|
||||
{ code: '<object title="An object" />' },
|
||||
|
||||
// DEFAULT <area> TESTS
|
||||
{ code: '<area aria-label="foo" />' },
|
||||
{ code: '<area aria-labelledby="id1" />' },
|
||||
{ code: '<area alt="" />' },
|
||||
{ code: '<area alt="This is descriptive!" />' },
|
||||
{ code: '<area alt={altText} />' },
|
||||
{ code: '<Area />' },
|
||||
|
||||
// DEFAULT <input type="image"> TESTS
|
||||
{ code: '<input />' },
|
||||
{ code: '<input type="foo" />' },
|
||||
{ code: '<input type="image" aria-label="foo" />' },
|
||||
{ code: '<input type="image" aria-labelledby="id1" />' },
|
||||
{ code: '<input type="image" alt="" />' },
|
||||
{ code: '<input type="image" alt="This is descriptive!" />' },
|
||||
{ code: '<input type="image" alt={altText} />' },
|
||||
{ code: '<InputImage />' },
|
||||
|
||||
// CUSTOM ELEMENT TESTS FOR ARRAY OPTION TESTS
|
||||
{ code: '<Thumbnail alt="foo" />;', options: array },
|
||||
{ code: '<Thumbnail alt={"foo"} />;', options: array },
|
||||
{ code: '<Thumbnail alt={alt} />;', options: array },
|
||||
{ code: '<Thumbnail ALT="foo" />;', options: array },
|
||||
{ code: '<Thumbnail ALT={`This is the ${alt} text`} />;', options: array },
|
||||
{ code: '<Thumbnail ALt="foo" />;', options: array },
|
||||
{ code: '<Thumbnail alt="foo" salt={undefined} />;', options: array },
|
||||
{ code: '<Thumbnail {...this.props} alt="foo" />', options: array },
|
||||
{ code: '<thumbnail />', options: array },
|
||||
{ code: '<Thumbnail alt={function(e) {} } />', options: array },
|
||||
{ code: '<div alt={function(e) {} } />', options: array },
|
||||
{ code: '<Thumbnail alt={() => void 0} />', options: array },
|
||||
{ code: '<THUMBNAIL />', options: array },
|
||||
{ code: '<Thumbnail alt={alt || "foo" } />', options: array },
|
||||
{ code: '<Image alt="foo" />;', options: array },
|
||||
{ code: '<Image alt={"foo"} />;', options: array },
|
||||
{ code: '<Image alt={alt} />;', options: array },
|
||||
{ code: '<Image ALT="foo" />;', options: array },
|
||||
{ code: '<Image ALT={`This is the ${alt} text`} />;', options: array },
|
||||
{ code: '<Image ALt="foo" />;', options: array },
|
||||
{ code: '<Image alt="foo" salt={undefined} />;', options: array },
|
||||
{ code: '<Image {...this.props} alt="foo" />', options: array },
|
||||
{ code: '<image />', options: array },
|
||||
{ code: '<Image alt={function(e) {} } />', options: array },
|
||||
{ code: '<div alt={function(e) {} } />', options: array },
|
||||
{ code: '<Image alt={() => void 0} />', options: array },
|
||||
{ code: '<IMAGE />', options: array },
|
||||
{ code: '<Image alt={alt || "foo" } />', options: array },
|
||||
{ code: '<Object aria-label="foo" />', options: array },
|
||||
{ code: '<Object aria-labelledby="id1" />', options: array },
|
||||
{ code: '<Object>Foo</Object>', options: array },
|
||||
{ code: '<Object><p>This is descriptive!</p></Object>', options: array },
|
||||
{ code: '<Object title="An object" />', options: array },
|
||||
{ code: '<Area aria-label="foo" />', options: array },
|
||||
{ code: '<Area aria-labelledby="id1" />', options: array },
|
||||
{ code: '<Area alt="" />', options: array },
|
||||
{ code: '<Area alt="This is descriptive!" />', options: array },
|
||||
{ code: '<Area alt={altText} />', options: array },
|
||||
{ code: '<InputImage aria-label="foo" />', options: array },
|
||||
{ code: '<InputImage aria-labelledby="id1" />', options: array },
|
||||
{ code: '<InputImage alt="" />', options: array },
|
||||
{ code: '<InputImage alt="This is descriptive!" />', options: array },
|
||||
{ code: '<InputImage alt={altText} />', options: array },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// DEFAULT ELEMENT 'img' TESTS
|
||||
{ code: '<img />;', errors: [missingPropError('img')] },
|
||||
{ code: '<img alt />;', errors: [altValueError('img')] },
|
||||
{ code: '<img alt={undefined} />;', errors: [altValueError('img')] },
|
||||
{ code: '<img src="xyz" />', errors: [missingPropError('img')] },
|
||||
{ code: '<img role />', errors: [missingPropError('img')] },
|
||||
{ code: '<img {...this.props} />', errors: [missingPropError('img')] },
|
||||
{ code: '<img alt={false || false} />', errors: [altValueError('img')] },
|
||||
{ code: '<img alt={undefined} role="presentation" />;', errors: [altValueError('img')] },
|
||||
{ code: '<img alt role="presentation" />;', errors: [altValueError('img')] },
|
||||
{ code: '<img role="presentation" />;', errors: [preferAltError()] },
|
||||
{ code: '<img role="none" />;', errors: [preferAltError()] },
|
||||
{ code: '<img aria-label={undefined} />', errors: [ariaLabelValueError] },
|
||||
{ code: '<img aria-labelledby={undefined} />', errors: [ariaLabelledbyValueError] },
|
||||
{ code: '<img aria-label="" />', errors: [ariaLabelValueError] },
|
||||
{ code: '<img aria-labelledby="" />', errors: [ariaLabelledbyValueError] },
|
||||
|
||||
// DEFAULT ELEMENT 'object' TESTS
|
||||
{ code: '<object />', errors: [objectError] },
|
||||
{ code: '<object><div aria-hidden /></object>', errors: [objectError] },
|
||||
{ code: '<object title={undefined} />', errors: [objectError] },
|
||||
{ code: '<object aria-label="" />', errors: [objectError] },
|
||||
{ code: '<object aria-labelledby="" />', errors: [objectError] },
|
||||
{ code: '<object aria-label={undefined} />', errors: [objectError] },
|
||||
{ code: '<object aria-labelledby={undefined} />', errors: [objectError] },
|
||||
|
||||
// DEFAULT ELEMENT 'area' TESTS
|
||||
{ code: '<area />', errors: [areaError] },
|
||||
{ code: '<area alt />', errors: [areaError] },
|
||||
{ code: '<area alt={undefined} />', errors: [areaError] },
|
||||
{ code: '<area src="xyz" />', errors: [areaError] },
|
||||
{ code: '<area {...this.props} />', errors: [areaError] },
|
||||
{ code: '<area aria-label="" />', errors: [areaError] },
|
||||
{ code: '<area aria-label={undefined} />', errors: [areaError] },
|
||||
{ code: '<area aria-labelledby="" />', errors: [areaError] },
|
||||
{ code: '<area aria-labelledby={undefined} />', errors: [areaError] },
|
||||
|
||||
// DEFAULT ELEMENT 'input type="image"' TESTS
|
||||
{ code: '<input type="image" />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" alt />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" alt={undefined} />', errors: [inputImageError] },
|
||||
{ code: '<input type="image">Foo</input>', errors: [inputImageError] },
|
||||
{ code: '<input type="image" {...this.props} />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" aria-label="" />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" aria-label={undefined} />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" aria-labelledby="" />', errors: [inputImageError] },
|
||||
{ code: '<input type="image" aria-labelledby={undefined} />', errors: [inputImageError] },
|
||||
|
||||
// CUSTOM ELEMENT TESTS FOR ARRAY OPTION TESTS
|
||||
{
|
||||
code: '<Thumbnail />;',
|
||||
errors: [missingPropError('Thumbnail')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Thumbnail alt />;',
|
||||
errors: [altValueError('Thumbnail')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Thumbnail alt={undefined} />;',
|
||||
errors: [altValueError('Thumbnail')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Thumbnail src="xyz" />',
|
||||
errors: [missingPropError('Thumbnail')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Thumbnail {...this.props} />',
|
||||
errors: [missingPropError('Thumbnail')],
|
||||
options: array,
|
||||
},
|
||||
{ code: '<Image />;', errors: [missingPropError('Image')], options: array },
|
||||
{ code: '<Image alt />;', errors: [altValueError('Image')], options: array },
|
||||
{
|
||||
code: '<Image alt={undefined} />;',
|
||||
errors: [altValueError('Image')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Image src="xyz" />',
|
||||
errors: [missingPropError('Image')],
|
||||
options: array,
|
||||
},
|
||||
{
|
||||
code: '<Image {...this.props} />',
|
||||
errors: [missingPropError('Image')],
|
||||
options: array,
|
||||
},
|
||||
{ code: '<Object />', errors: [objectError], options: array },
|
||||
{ code: '<Object><div aria-hidden /></Object>', errors: [objectError], options: array },
|
||||
{ code: '<Object title={undefined} />', errors: [objectError], options: array },
|
||||
{ code: '<Area />', errors: [areaError], options: array },
|
||||
{ code: '<Area alt />', errors: [areaError], options: array },
|
||||
{ code: '<Area alt={undefined} />', errors: [areaError], options: array },
|
||||
{ code: '<Area src="xyz" />', errors: [areaError], options: array },
|
||||
{ code: '<Area {...this.props} />', errors: [areaError], options: array },
|
||||
{ code: '<InputImage />', errors: [inputImageError], options: array },
|
||||
{ code: '<InputImage alt />', errors: [inputImageError], options: array },
|
||||
{ code: '<InputImage alt={undefined} />', errors: [inputImageError], options: array },
|
||||
{ code: '<InputImage>Foo</InputImage>', errors: [inputImageError], options: array },
|
||||
{ code: '<InputImage {...this.props} />', errors: [inputImageError], options: array },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
41
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/anchor-has-content-test.js
generated
vendored
Normal file
41
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/anchor-has-content-test.js
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce anchor elements to contain accessible content.
|
||||
* @author Lisa Ring & Niklas Holmberg
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/anchor-has-content';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Anchors must have content and the content must be accessible by a screen reader.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('anchor-has-content', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<a>Foo</a>' },
|
||||
{ code: '<a><Bar /></a>' },
|
||||
{ code: '<a>{foo}</a>' },
|
||||
{ code: '<a>{foo.bar}</a>' },
|
||||
{ code: '<a dangerouslySetInnerHTML={{ __html: "foo" }} />' },
|
||||
{ code: '<a children={children} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<a />', errors: [expectedError] },
|
||||
{ code: '<a><Bar aria-hidden /></a>', errors: [expectedError] },
|
||||
{ code: '<a>{undefined}</a>', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
542
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/anchor-is-valid-test.js
generated
vendored
Normal file
542
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/anchor-is-valid-test.js
generated
vendored
Normal file
|
@ -0,0 +1,542 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Performs validity check on anchor hrefs. Warns when anchors are used as buttons.
|
||||
* @author Almero Steyn
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/anchor-is-valid';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const preferButtonErrorMessage = 'Anchor used as a button. Anchors are primarily expected to navigate. Use the button element instead. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md';
|
||||
|
||||
const noHrefErrorMessage = 'The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md';
|
||||
|
||||
const invalidHrefErrorMessage = 'The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md';
|
||||
|
||||
const preferButtonexpectedError = {
|
||||
message: preferButtonErrorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
const noHrefexpectedError = {
|
||||
message: noHrefErrorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
const invalidHrefexpectedError = {
|
||||
message: invalidHrefErrorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const components = [{
|
||||
components: ['Anchor', 'Link'],
|
||||
}];
|
||||
const specialLink = [{
|
||||
specialLink: ['hrefLeft', 'hrefRight'],
|
||||
}];
|
||||
const noHrefAspect = [{
|
||||
aspects: ['noHref'],
|
||||
}];
|
||||
const invalidHrefAspect = [{
|
||||
aspects: ['invalidHref'],
|
||||
}];
|
||||
const preferButtonAspect = [{
|
||||
aspects: ['preferButton'],
|
||||
}];
|
||||
const noHrefInvalidHrefAspect = [{
|
||||
aspects: ['noHref', 'invalidHref'],
|
||||
}];
|
||||
const noHrefPreferButtonAspect = [{
|
||||
aspects: ['noHref', 'preferButton'],
|
||||
}];
|
||||
const preferButtonInvalidHrefAspect = [{
|
||||
aspects: ['preferButton', 'invalidHref'],
|
||||
}];
|
||||
|
||||
const componentsAndSpecialLink = [{
|
||||
components: ['Anchor'],
|
||||
specialLink: ['hrefLeft'],
|
||||
}];
|
||||
|
||||
const componentsAndSpecialLinkAndInvalidHrefAspect = [{
|
||||
components: ['Anchor'],
|
||||
specialLink: ['hrefLeft'],
|
||||
aspects: ['invalidHref'],
|
||||
}];
|
||||
|
||||
const componentsAndSpecialLinkAndNoHrefAspect = [{
|
||||
components: ['Anchor'],
|
||||
specialLink: ['hrefLeft'],
|
||||
aspects: ['noHref'],
|
||||
}];
|
||||
|
||||
ruleTester.run('anchor-is-valid', rule, {
|
||||
valid: [
|
||||
// DEFAULT ELEMENT 'a' TESTS
|
||||
{ code: '<Anchor />;' },
|
||||
{ code: '<a {...props} />' },
|
||||
{ code: '<a href="foo" />' },
|
||||
{ code: '<a href={foo} />' },
|
||||
{ code: '<a href="/foo" />' },
|
||||
{ code: '<a href="https://foo.bar.com" />' },
|
||||
{ code: '<div href="foo" />' },
|
||||
{ code: '<a href="javascript" />' },
|
||||
{ code: '<a href="javascriptFoo" />' },
|
||||
{ code: '<a href={`#foo`}/>' },
|
||||
{ code: '<a href={"foo"}/>' },
|
||||
{ code: '<a href={"javascript"}/>' },
|
||||
{ code: '<a href={`#javascript`}/>' },
|
||||
{ code: '<a href="#foo" />' },
|
||||
{ code: '<a href="#javascript" />' },
|
||||
{ code: '<a href="#javascriptFoo" />' },
|
||||
{ code: '<UX.Layout>test</UX.Layout>' },
|
||||
{ code: '<a href={this} />' },
|
||||
|
||||
// CUSTOM ELEMENT TEST FOR ARRAY OPTION
|
||||
{ code: '<Anchor {...props} />', options: components },
|
||||
{ code: '<Anchor href="foo" />', options: components },
|
||||
{ code: '<Anchor href={foo} />', options: components },
|
||||
{ code: '<Anchor href="/foo" />', options: components },
|
||||
{ code: '<Anchor href="https://foo.bar.com" />', options: components },
|
||||
{ code: '<div href="foo" />', options: components },
|
||||
{ code: '<Anchor href={`#foo`}/>', options: components },
|
||||
{ code: '<Anchor href={"foo"}/>', options: components },
|
||||
{ code: '<Anchor href="#foo" />', options: components },
|
||||
{ code: '<Link {...props} />', options: components },
|
||||
{ code: '<Link href="foo" />', options: components },
|
||||
{ code: '<Link href={foo} />', options: components },
|
||||
{ code: '<Link href="/foo" />', options: components },
|
||||
{ code: '<Link href="https://foo.bar.com" />', options: components },
|
||||
{ code: '<div href="foo" />', options: components },
|
||||
{ code: '<Link href={`#foo`}/>', options: components },
|
||||
{ code: '<Link href={"foo"}/>', options: components },
|
||||
{ code: '<Link href="#foo" />', options: components },
|
||||
|
||||
// CUSTOM PROP TESTS
|
||||
{ code: '<a {...props} />', options: specialLink },
|
||||
{ code: '<a hrefLeft="foo" />', options: specialLink },
|
||||
{ code: '<a hrefLeft={foo} />', options: specialLink },
|
||||
{ code: '<a hrefLeft="/foo" />', options: specialLink },
|
||||
{ code: '<a hrefLeft="https://foo.bar.com" />', options: specialLink },
|
||||
{ code: '<div hrefLeft="foo" />', options: specialLink },
|
||||
{ code: '<a hrefLeft={`#foo`}/>', options: specialLink },
|
||||
{ code: '<a hrefLeft={"foo"}/>', options: specialLink },
|
||||
{ code: '<a hrefLeft="#foo" />', options: specialLink },
|
||||
{ code: '<UX.Layout>test</UX.Layout>', options: specialLink },
|
||||
{ code: '<a hrefRight={this} />', options: specialLink },
|
||||
{ code: '<a {...props} />', options: specialLink },
|
||||
{ code: '<a hrefRight="foo" />', options: specialLink },
|
||||
{ code: '<a hrefRight={foo} />', options: specialLink },
|
||||
{ code: '<a hrefRight="/foo" />', options: specialLink },
|
||||
{ code: '<a hrefRight="https://foo.bar.com" />', options: specialLink },
|
||||
{ code: '<div hrefRight="foo" />', options: specialLink },
|
||||
{ code: '<a hrefRight={`#foo`}/>', options: specialLink },
|
||||
{ code: '<a hrefRight={"foo"}/>', options: specialLink },
|
||||
{ code: '<a hrefRight="#foo" />', options: specialLink },
|
||||
{ code: '<UX.Layout>test</UX.Layout>', options: specialLink },
|
||||
{ code: '<a hrefRight={this} />', options: specialLink },
|
||||
|
||||
// CUSTOM BOTH COMPONENTS AND SPECIALLINK TESTS
|
||||
{ code: '<Anchor {...props} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="foo" />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={foo} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="/foo" />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="https://foo.bar.com" />', options: componentsAndSpecialLink },
|
||||
{ code: '<div hrefLeft="foo" />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={`#foo`}/>', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={"foo"}/>', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="#foo" />', options: componentsAndSpecialLink },
|
||||
{ code: '<UX.Layout>test</UX.Layout>', options: componentsAndSpecialLink },
|
||||
|
||||
// WITH ONCLICK
|
||||
// DEFAULT ELEMENT 'a' TESTS
|
||||
{ code: '<a {...props} onClick={() => void 0} />' },
|
||||
{ code: '<a href="foo" onClick={() => void 0} />' },
|
||||
{ code: '<a href={foo} onClick={() => void 0} />' },
|
||||
{ code: '<a href="/foo" onClick={() => void 0} />' },
|
||||
{ code: '<a href="https://foo.bar.com" onClick={() => void 0} />' },
|
||||
{ code: '<div href="foo" onClick={() => void 0} />' },
|
||||
{ code: '<a href={`#foo`} onClick={() => void 0} />' },
|
||||
{ code: '<a href={"foo"} onClick={() => void 0} />' },
|
||||
{ code: '<a href="#foo" onClick={() => void 0} />' },
|
||||
{ code: '<a href={this} onClick={() => void 0} />' },
|
||||
|
||||
// CUSTOM ELEMENT TEST FOR ARRAY OPTION
|
||||
{ code: '<Anchor {...props} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href="foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href={foo} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href="/foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href="https://foo.bar.com" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href={`#foo`} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href={"foo"} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Anchor href="#foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link {...props} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href="foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href={foo} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href="/foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href="https://foo.bar.com" onClick={() => void 0} />', options: components },
|
||||
{ code: '<div href="foo" onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href={`#foo`} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href={"foo"} onClick={() => void 0} />', options: components },
|
||||
{ code: '<Link href="#foo" onClick={() => void 0} />', options: components },
|
||||
|
||||
// CUSTOM PROP TESTS
|
||||
{ code: '<a {...props} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft="foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft={foo} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft="/foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft href="https://foo.bar.com" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<div hrefLeft="foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft={`#foo`} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft={"foo"} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefLeft="#foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight={this} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a {...props} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight="foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight={foo} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight="/foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight href="https://foo.bar.com" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<div hrefRight="foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight={`#foo`} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight={"foo"} onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight="#foo" onClick={() => void 0} />', options: specialLink },
|
||||
{ code: '<a hrefRight={this} onClick={() => void 0} />', options: specialLink },
|
||||
|
||||
// CUSTOM BOTH COMPONENTS AND SPECIALLINK TESTS
|
||||
{ code: '<Anchor {...props} onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="foo" onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={foo} onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="/foo" onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{
|
||||
code: '<Anchor hrefLeft href="https://foo.bar.com" onClick={() => void 0} />',
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
{ code: '<Anchor hrefLeft={`#foo`} onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={"foo"} onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="#foo" onClick={() => void 0} />', options: componentsAndSpecialLink },
|
||||
|
||||
// WITH ASPECTS TESTS
|
||||
// NO HREF
|
||||
{ code: '<a />', options: invalidHrefAspect },
|
||||
{ code: '<a href={undefined} />', options: invalidHrefAspect },
|
||||
{ code: '<a href={null} />', options: invalidHrefAspect },
|
||||
{ code: '<a />', options: preferButtonAspect },
|
||||
{ code: '<a href={undefined} />', options: preferButtonAspect },
|
||||
{ code: '<a href={null} />', options: preferButtonAspect },
|
||||
{ code: '<a />', options: preferButtonInvalidHrefAspect },
|
||||
{ code: '<a href={undefined} />', options: preferButtonInvalidHrefAspect },
|
||||
{ code: '<a href={null} />', options: preferButtonInvalidHrefAspect },
|
||||
|
||||
// INVALID HREF
|
||||
{ code: '<a href="" />;', options: preferButtonAspect },
|
||||
{ code: '<a href="#" />', options: preferButtonAspect },
|
||||
{ code: '<a href={"#"} />', options: preferButtonAspect },
|
||||
{ code: '<a href="javascript:void(0)" />', options: preferButtonAspect },
|
||||
{ code: '<a href={"javascript:void(0)"} />', options: preferButtonAspect },
|
||||
{ code: '<a href="" />;', options: noHrefAspect },
|
||||
{ code: '<a href="#" />', options: noHrefAspect },
|
||||
{ code: '<a href={"#"} />', options: noHrefAspect },
|
||||
{ code: '<a href="javascript:void(0)" />', options: noHrefAspect },
|
||||
{ code: '<a href={"javascript:void(0)"} />', options: noHrefAspect },
|
||||
{ code: '<a href="" />;', options: noHrefPreferButtonAspect },
|
||||
{ code: '<a href="#" />', options: noHrefPreferButtonAspect },
|
||||
{ code: '<a href={"#"} />', options: noHrefPreferButtonAspect },
|
||||
{ code: '<a href="javascript:void(0)" />', options: noHrefPreferButtonAspect },
|
||||
{ code: '<a href={"javascript:void(0)"} />', options: noHrefPreferButtonAspect },
|
||||
|
||||
// SHOULD BE BUTTON
|
||||
{ code: '<a onClick={() => void 0} />', options: invalidHrefAspect },
|
||||
{ code: '<a href="#" onClick={() => void 0} />', options: noHrefAspect },
|
||||
{ code: '<a href="javascript:void(0)" onClick={() => void 0} />', options: noHrefAspect },
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: noHrefAspect,
|
||||
},
|
||||
|
||||
// CUSTOM COMPONENTS AND SPECIALLINK AND ASPECT
|
||||
{ code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
{ code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
{ code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
{ code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
{ code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
{ code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
|
||||
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// DEFAULT ELEMENT 'a' TESTS
|
||||
// NO HREF
|
||||
{ code: '<a />', errors: [noHrefexpectedError] },
|
||||
{ code: '<a href={undefined} />', errors: [noHrefexpectedError] },
|
||||
{ code: '<a href={null} />', errors: [noHrefexpectedError] },
|
||||
// INVALID HREF
|
||||
{ code: '<a href="" />;', errors: [invalidHrefexpectedError] },
|
||||
{ code: '<a href="#" />', errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href={"#"} />', errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="javascript:void(0)" />', errors: [invalidHrefexpectedError] },
|
||||
{ code: '<a href={"javascript:void(0)"} />', errors: [invalidHrefexpectedError] },
|
||||
// SHOULD BE BUTTON
|
||||
{ code: '<a onClick={() => void 0} />', errors: [preferButtonexpectedError] },
|
||||
{ code: '<a href="#" onClick={() => void 0} />', errors: [preferButtonexpectedError] },
|
||||
{ code: '<a href="javascript:void(0)" onClick={() => void 0} />', errors: [preferButtonexpectedError] },
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
},
|
||||
|
||||
// CUSTOM ELEMENT TEST FOR ARRAY OPTION
|
||||
// NO HREF
|
||||
{ code: '<Link />', errors: [noHrefexpectedError], options: components },
|
||||
{ code: '<Link href={undefined} />', errors: [noHrefexpectedError], options: components },
|
||||
{ code: '<Link href={null} />', errors: [noHrefexpectedError], options: components },
|
||||
// INVALID HREF
|
||||
{ code: '<Link href="" />', errors: [invalidHrefexpectedError], options: components },
|
||||
{ code: '<Link href="#" />', errors: [invalidHrefErrorMessage], options: components },
|
||||
{ code: '<Link href={"#"} />', errors: [invalidHrefErrorMessage], options: components },
|
||||
{ code: '<Link href="javascript:void(0)" />', errors: [invalidHrefexpectedError], options: components },
|
||||
{ code: '<Link href={"javascript:void(0)"} />', errors: [invalidHrefexpectedError], options: components },
|
||||
{ code: '<Anchor href="" />', errors: [invalidHrefexpectedError], options: components },
|
||||
{ code: '<Anchor href="#" />', errors: [invalidHrefErrorMessage], options: components },
|
||||
{ code: '<Anchor href={"#"} />', errors: [invalidHrefErrorMessage], options: components },
|
||||
{ code: '<Anchor href="javascript:void(0)" />', errors: [invalidHrefexpectedError], options: components },
|
||||
{ code: '<Anchor href={"javascript:void(0)"} />', errors: [invalidHrefexpectedError], options: components },
|
||||
// SHOULD BE BUTTON
|
||||
{ code: '<Link onClick={() => void 0} />', errors: [preferButtonexpectedError], options: components },
|
||||
{ code: '<Link href="#" onClick={() => void 0} />', errors: [preferButtonexpectedError], options: components },
|
||||
{
|
||||
code: '<Link href="javascript:void(0)" onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: components,
|
||||
},
|
||||
{
|
||||
code: '<Link href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: components,
|
||||
},
|
||||
{ code: '<Anchor onClick={() => void 0} />', errors: [preferButtonexpectedError], options: components },
|
||||
{ code: '<Anchor href="#" onClick={() => void 0} />', errors: [preferButtonexpectedError], options: components },
|
||||
{
|
||||
code: '<Anchor href="javascript:void(0)" onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: components,
|
||||
},
|
||||
{
|
||||
code: '<Anchor href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: components,
|
||||
},
|
||||
|
||||
// CUSTOM PROP TESTS
|
||||
// NO HREF
|
||||
{ code: '<a hrefLeft={undefined} />', errors: [noHrefexpectedError], options: specialLink },
|
||||
{ code: '<a hrefLeft={null} />', errors: [noHrefexpectedError], options: specialLink },
|
||||
// INVALID HREF
|
||||
{ code: '<a hrefLeft="" />;', errors: [invalidHrefexpectedError], options: specialLink },
|
||||
{ code: '<a hrefLeft="#" />', errors: [invalidHrefErrorMessage], options: specialLink },
|
||||
{ code: '<a hrefLeft={"#"} />', errors: [invalidHrefErrorMessage], options: specialLink },
|
||||
{ code: '<a hrefLeft="javascript:void(0)" />', errors: [invalidHrefexpectedError], options: specialLink },
|
||||
{ code: '<a hrefLeft={"javascript:void(0)"} />', errors: [invalidHrefexpectedError], options: specialLink },
|
||||
// SHOULD BE BUTTON
|
||||
{ code: '<a hrefLeft="#" onClick={() => void 0} />', errors: [preferButtonexpectedError], options: specialLink },
|
||||
{
|
||||
code: '<a hrefLeft="javascript:void(0)" onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: specialLink,
|
||||
},
|
||||
{
|
||||
code: '<a hrefLeft={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: specialLink,
|
||||
},
|
||||
|
||||
// CUSTOM BOTH COMPONENTS AND SPECIALLINK TESTS
|
||||
// NO HREF
|
||||
{ code: '<Anchor Anchor={undefined} />', errors: [noHrefexpectedError], options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={null} />', errors: [noHrefexpectedError], options: componentsAndSpecialLink },
|
||||
// INVALID HREF
|
||||
{ code: '<Anchor hrefLeft="" />;', errors: [invalidHrefexpectedError], options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft="#" />', errors: [invalidHrefErrorMessage], options: componentsAndSpecialLink },
|
||||
{ code: '<Anchor hrefLeft={"#"} />', errors: [invalidHrefErrorMessage], options: componentsAndSpecialLink },
|
||||
{
|
||||
code: '<Anchor hrefLeft="javascript:void(0)" />',
|
||||
errors: [invalidHrefexpectedError],
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={"javascript:void(0)"} />',
|
||||
errors: [invalidHrefexpectedError],
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
// SHOULD BE BUTTON
|
||||
{
|
||||
code: '<Anchor hrefLeft="#" onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft="javascript:void(0)" onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
errors: [preferButtonexpectedError],
|
||||
options: componentsAndSpecialLink,
|
||||
},
|
||||
|
||||
// WITH ASPECTS TESTS
|
||||
// NO HREF
|
||||
{ code: '<a />', options: noHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a />', options: noHrefPreferButtonAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a />', options: noHrefInvalidHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={undefined} />', options: noHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={undefined} />', options: noHrefPreferButtonAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={undefined} />', options: noHrefInvalidHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={null} />', options: noHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={null} />', options: noHrefPreferButtonAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href={null} />', options: noHrefInvalidHrefAspect, errors: [noHrefErrorMessage] },
|
||||
|
||||
// INVALID HREF
|
||||
{ code: '<a href="" />;', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="" />;', options: noHrefInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="" />;', options: preferButtonInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="#" />;', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="#" />;', options: noHrefInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="#" />;', options: preferButtonInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href={"#"} />;', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href={"#"} />;', options: noHrefInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href={"#"} />;', options: preferButtonInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="javascript:void(0)" />;', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href="javascript:void(0)" />;', options: noHrefInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{
|
||||
code: '<a href="javascript:void(0)" />;',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
{ code: '<a href={"javascript:void(0)"} />;', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{ code: '<a href={"javascript:void(0)"} />;', options: noHrefInvalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} />;',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
|
||||
// SHOULD BE BUTTON
|
||||
{ code: '<a onClick={() => void 0} />', options: preferButtonAspect, errors: [preferButtonErrorMessage] },
|
||||
{
|
||||
code: '<a onClick={() => void 0} />',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{ code: '<a onClick={() => void 0} />', options: noHrefPreferButtonAspect, errors: [preferButtonErrorMessage] },
|
||||
{ code: '<a onClick={() => void 0} />', options: noHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a onClick={() => void 0} />', options: noHrefInvalidHrefAspect, errors: [noHrefErrorMessage] },
|
||||
{ code: '<a href="#" onClick={() => void 0} />', options: preferButtonAspect, errors: [preferButtonErrorMessage] },
|
||||
{
|
||||
code: '<a href="#" onClick={() => void 0} />',
|
||||
options: noHrefPreferButtonAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="#" onClick={() => void 0} />',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{ code: '<a href="#" onClick={() => void 0} />', options: invalidHrefAspect, errors: [invalidHrefErrorMessage] },
|
||||
{
|
||||
code: '<a href="#" onClick={() => void 0} />',
|
||||
options: noHrefInvalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="javascript:void(0)" onClick={() => void 0} />',
|
||||
options: preferButtonAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="javascript:void(0)" onClick={() => void 0} />',
|
||||
options: noHrefPreferButtonAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="javascript:void(0)" onClick={() => void 0} />',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="javascript:void(0)" onClick={() => void 0} />',
|
||||
options: invalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href="javascript:void(0)" onClick={() => void 0} />',
|
||||
options: noHrefInvalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: preferButtonAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: noHrefPreferButtonAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: preferButtonInvalidHrefAspect,
|
||||
errors: [preferButtonErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: invalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<a href={"javascript:void(0)"} onClick={() => void 0} />',
|
||||
options: noHrefInvalidHrefAspect,
|
||||
errors: [invalidHrefErrorMessage],
|
||||
},
|
||||
|
||||
// CUSTOM COMPONENTS AND SPECIALLINK AND ASPECT
|
||||
{
|
||||
code: '<Anchor hrefLeft={undefined} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={null} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={undefined} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={null} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={undefined} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
{
|
||||
code: '<Anchor hrefLeft={null} />',
|
||||
options: componentsAndSpecialLinkAndNoHrefAspect,
|
||||
errors: [noHrefErrorMessage],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
85
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-activedescendant-has-tabindex-test.js
generated
vendored
Normal file
85
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-activedescendant-has-tabindex-test.js
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @fileoverview Enforce elements with aria-activedescendant are tabbable.
|
||||
* @author Jesse Beach <@jessebeach>
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/aria-activedescendant-has-tabindex';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'An element that manages focus with `aria-activedescendant` must have a tabindex',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('aria-activedescendant-has-tabindex', rule, {
|
||||
valid: [
|
||||
{
|
||||
code: '<CustomComponent />;',
|
||||
},
|
||||
{
|
||||
code: '<CustomComponent aria-activedescendant={someID} />;',
|
||||
},
|
||||
{
|
||||
code: '<CustomComponent aria-activedescendant={someID} tabIndex={0} />;',
|
||||
},
|
||||
{
|
||||
code: '<CustomComponent aria-activedescendant={someID} tabIndex={-1} />;',
|
||||
},
|
||||
{
|
||||
code: '<div />;',
|
||||
},
|
||||
{
|
||||
code: '<input />;',
|
||||
},
|
||||
{
|
||||
code: '<div tabIndex={0} />;',
|
||||
},
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} tabIndex={0} />;',
|
||||
},
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} tabIndex="0" />;',
|
||||
},
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} tabIndex={1} />;',
|
||||
},
|
||||
{
|
||||
code: '<input aria-activedescendant={someID} />;',
|
||||
},
|
||||
{
|
||||
code: '<input aria-activedescendant={someID} tabIndex={1} />;',
|
||||
},
|
||||
{
|
||||
code: '<input aria-activedescendant={someID} tabIndex={0} />;',
|
||||
},
|
||||
{
|
||||
code: '<input aria-activedescendant={someID} tabIndex={-1} />;',
|
||||
},
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} tabIndex={-1} />;',
|
||||
},
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} tabIndex="-1" />;',
|
||||
},
|
||||
{
|
||||
code: '<input aria-activedescendant={someID} tabIndex={-1} />;',
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{
|
||||
code: '<div aria-activedescendant={someID} />;',
|
||||
errors: [expectedError],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
69
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-props-test.js
generated
vendored
Normal file
69
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-props-test.js
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce all aria-* properties are valid.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { aria } from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/aria-props';
|
||||
import getSuggestion from '../../../src/util/getSuggestion';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
const ariaAttributes = [...aria.keys()];
|
||||
|
||||
const errorMessage = (name) => {
|
||||
const suggestions = getSuggestion(name, ariaAttributes);
|
||||
const message = `${name}: This attribute is an invalid ARIA attribute.`;
|
||||
|
||||
if (suggestions.length > 0) {
|
||||
return {
|
||||
type: 'JSXAttribute',
|
||||
message: `${message} Did you mean to use ${suggestions}?`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'JSXAttribute',
|
||||
message,
|
||||
};
|
||||
};
|
||||
|
||||
// Create basic test cases using all valid role types.
|
||||
const basicValidityTests = ariaAttributes.map((prop) => ({
|
||||
code: `<div ${prop.toLowerCase()}="foobar" />`,
|
||||
}));
|
||||
|
||||
ruleTester.run('aria-props', rule, {
|
||||
valid: [
|
||||
// Variables should pass, as we are only testing literals.
|
||||
{ code: '<div />' },
|
||||
{ code: '<div></div>' },
|
||||
{ code: '<div aria="wee"></div>' }, // Needs aria-*
|
||||
{ code: '<div abcARIAdef="true"></div>' },
|
||||
{ code: '<div fooaria-foobar="true"></div>' },
|
||||
{ code: '<div fooaria-hidden="true"></div>' },
|
||||
{ code: '<Bar baz />' },
|
||||
{ code: '<input type="text" aria-errormessage="foobar" />' },
|
||||
].concat(basicValidityTests).map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div aria-="foobar" />', errors: [errorMessage('aria-')] },
|
||||
{
|
||||
code: '<div aria-labeledby="foobar" />',
|
||||
errors: [errorMessage('aria-labeledby')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-skldjfaria-klajsd="foobar" />',
|
||||
errors: [errorMessage('aria-skldjfaria-klajsd')],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
306
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-proptypes-test.js
generated
vendored
Normal file
306
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-proptypes-test.js
generated
vendored
Normal file
|
@ -0,0 +1,306 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce ARIA state and property values are valid.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { aria } from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/aria-proptypes';
|
||||
|
||||
const { validityCheck } = rule;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = (name) => {
|
||||
const {
|
||||
type,
|
||||
values: permittedValues,
|
||||
} = aria.get(name.toLowerCase());
|
||||
|
||||
switch (type) {
|
||||
case 'tristate':
|
||||
return `The value for ${name} must be a boolean or the string "mixed".`;
|
||||
case 'token':
|
||||
return `The value for ${name} must be a single token from the following: ${permittedValues}.`;
|
||||
case 'tokenlist':
|
||||
return `The value for ${name} must be a list of one or more \
|
||||
tokens from the following: ${permittedValues}.`;
|
||||
case 'idlist':
|
||||
return `The value for ${name} must be a list of strings that represent DOM element IDs (idlist)`;
|
||||
case 'id':
|
||||
return `The value for ${name} must be a string that represents a DOM element ID`;
|
||||
case 'boolean':
|
||||
case 'string':
|
||||
case 'integer':
|
||||
case 'number':
|
||||
default:
|
||||
return `The value for ${name} must be a ${type}.`;
|
||||
}
|
||||
};
|
||||
|
||||
describe('validityCheck', () => {
|
||||
it('should false for an unknown expected type', () => {
|
||||
expect(validityCheck(
|
||||
null,
|
||||
null,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
ruleTester.run('aria-proptypes', rule, {
|
||||
valid: [
|
||||
// DON'T TEST INVALID ARIA-* PROPS
|
||||
{ code: '<div aria-foo="true" />' },
|
||||
{ code: '<div abcaria-foo="true" />' },
|
||||
|
||||
// BOOLEAN
|
||||
{ code: '<div aria-hidden={true} />' },
|
||||
{ code: '<div aria-hidden="true" />' },
|
||||
{ code: '<div aria-hidden={"false"} />' },
|
||||
{ code: '<div aria-hidden={!false} />' },
|
||||
{ code: '<div aria-hidden />' },
|
||||
{ code: '<div aria-hidden={false} />' },
|
||||
{ code: '<div aria-hidden={!true} />' },
|
||||
{ code: '<div aria-hidden={!"yes"} />' },
|
||||
{ code: '<div aria-hidden={foo} />' },
|
||||
{ code: '<div aria-hidden={foo.bar} />' },
|
||||
{ code: '<div aria-hidden={null} />' },
|
||||
{ code: '<div aria-hidden={undefined} />' },
|
||||
{ code: '<div aria-hidden={<div />} />' },
|
||||
|
||||
// STRING
|
||||
{ code: '<div aria-label="Close" />' },
|
||||
{ code: '<div aria-label={`Close`} />' },
|
||||
{ code: '<div aria-label={foo} />' },
|
||||
{ code: '<div aria-label={foo.bar} />' },
|
||||
{ code: '<div aria-label={null} />' },
|
||||
{ code: '<div aria-label={undefined} />' },
|
||||
{ code: '<input aria-invalid={error ? "true" : "false"} />' },
|
||||
{ code: '<input aria-invalid={undefined ? "true" : "false"} />' },
|
||||
|
||||
// TRISTATE
|
||||
{ code: '<div aria-checked={true} />' },
|
||||
{ code: '<div aria-checked="true" />' },
|
||||
{ code: '<div aria-checked={"false"} />' },
|
||||
{ code: '<div aria-checked={!false} />' },
|
||||
{ code: '<div aria-checked />' },
|
||||
{ code: '<div aria-checked={false} />' },
|
||||
{ code: '<div aria-checked={!true} />' },
|
||||
{ code: '<div aria-checked={!"yes"} />' },
|
||||
{ code: '<div aria-checked={foo} />' },
|
||||
{ code: '<div aria-checked={foo.bar} />' },
|
||||
{ code: '<div aria-checked="mixed" />' },
|
||||
{ code: '<div aria-checked={`mixed`} />' },
|
||||
{ code: '<div aria-checked={null} />' },
|
||||
{ code: '<div aria-checked={undefined} />' },
|
||||
|
||||
// INTEGER
|
||||
{ code: '<div aria-level={123} />' },
|
||||
{ code: '<div aria-level={-123} />' },
|
||||
{ code: '<div aria-level={+123} />' },
|
||||
{ code: '<div aria-level={~123} />' },
|
||||
{ code: '<div aria-level={"123"} />' },
|
||||
{ code: '<div aria-level={`123`} />' },
|
||||
{ code: '<div aria-level="123" />' },
|
||||
{ code: '<div aria-level={foo} />' },
|
||||
{ code: '<div aria-level={foo.bar} />' },
|
||||
{ code: '<div aria-level={null} />' },
|
||||
{ code: '<div aria-level={undefined} />' },
|
||||
|
||||
// NUMBER
|
||||
{ code: '<div aria-valuemax={123} />' },
|
||||
{ code: '<div aria-valuemax={-123} />' },
|
||||
{ code: '<div aria-valuemax={+123} />' },
|
||||
{ code: '<div aria-valuemax={~123} />' },
|
||||
{ code: '<div aria-valuemax={"123"} />' },
|
||||
{ code: '<div aria-valuemax={`123`} />' },
|
||||
{ code: '<div aria-valuemax="123" />' },
|
||||
{ code: '<div aria-valuemax={foo} />' },
|
||||
{ code: '<div aria-valuemax={foo.bar} />' },
|
||||
{ code: '<div aria-valuemax={null} />' },
|
||||
{ code: '<div aria-valuemax={undefined} />' },
|
||||
|
||||
// TOKEN
|
||||
{ code: '<div aria-sort="ascending" />' },
|
||||
{ code: '<div aria-sort="ASCENDING" />' },
|
||||
{ code: '<div aria-sort={"ascending"} />' },
|
||||
{ code: '<div aria-sort={`ascending`} />' },
|
||||
{ code: '<div aria-sort="descending" />' },
|
||||
{ code: '<div aria-sort={"descending"} />' },
|
||||
{ code: '<div aria-sort={`descending`} />' },
|
||||
{ code: '<div aria-sort="none" />' },
|
||||
{ code: '<div aria-sort={"none"} />' },
|
||||
{ code: '<div aria-sort={`none`} />' },
|
||||
{ code: '<div aria-sort="other" />' },
|
||||
{ code: '<div aria-sort={"other"} />' },
|
||||
{ code: '<div aria-sort={`other`} />' },
|
||||
{ code: '<div aria-sort={foo} />' },
|
||||
{ code: '<div aria-sort={foo.bar} />' },
|
||||
{ code: '<div aria-invalid={true} />' },
|
||||
{ code: '<div aria-invalid="true" />' },
|
||||
{ code: '<div aria-invalid={false} />' },
|
||||
{ code: '<div aria-invalid="false" />' },
|
||||
{ code: '<div aria-invalid="grammar" />' },
|
||||
{ code: '<div aria-invalid="spelling" />' },
|
||||
{ code: '<div aria-invalid={null} />' },
|
||||
{ code: '<div aria-invalid={undefined} />' },
|
||||
|
||||
// TOKENLIST
|
||||
{ code: '<div aria-relevant="additions" />' },
|
||||
{ code: '<div aria-relevant={"additions"} />' },
|
||||
{ code: '<div aria-relevant={`additions`} />' },
|
||||
{ code: '<div aria-relevant="additions removals" />' },
|
||||
{ code: '<div aria-relevant="additions additions" />' },
|
||||
{ code: '<div aria-relevant={"additions removals"} />' },
|
||||
{ code: '<div aria-relevant={`additions removals`} />' },
|
||||
{ code: '<div aria-relevant="additions removals text" />' },
|
||||
{ code: '<div aria-relevant={"additions removals text"} />' },
|
||||
{ code: '<div aria-relevant={`additions removals text`} />' },
|
||||
{ code: '<div aria-relevant="additions removals text all" />' },
|
||||
{ code: '<div aria-relevant={"additions removals text all"} />' },
|
||||
{ code: '<div aria-relevant={`removals additions text all`} />' },
|
||||
{ code: '<div aria-relevant={foo} />' },
|
||||
{ code: '<div aria-relevant={foo.bar} />' },
|
||||
{ code: '<div aria-relevant={null} />' },
|
||||
{ code: '<div aria-relevant={undefined} />' },
|
||||
|
||||
// ID
|
||||
{ code: '<div aria-activedescendant="ascending" />' },
|
||||
{ code: '<div aria-activedescendant="ASCENDING" />' },
|
||||
{ code: '<div aria-activedescendant={"ascending"} />' },
|
||||
{ code: '<div aria-activedescendant={`ascending`} />' },
|
||||
{ code: '<div aria-activedescendant="descending" />' },
|
||||
{ code: '<div aria-activedescendant={"descending"} />' },
|
||||
{ code: '<div aria-activedescendant={`descending`} />' },
|
||||
{ code: '<div aria-activedescendant="none" />' },
|
||||
{ code: '<div aria-activedescendant={"none"} />' },
|
||||
{ code: '<div aria-activedescendant={`none`} />' },
|
||||
{ code: '<div aria-activedescendant="other" />' },
|
||||
{ code: '<div aria-activedescendant={"other"} />' },
|
||||
{ code: '<div aria-activedescendant={`other`} />' },
|
||||
{ code: '<div aria-activedescendant={foo} />' },
|
||||
{ code: '<div aria-activedescendant={foo.bar} />' },
|
||||
{ code: '<div aria-activedescendant={null} />' },
|
||||
{ code: '<div aria-activedescendant={undefined} />' },
|
||||
|
||||
// IDLIST
|
||||
{ code: '<div aria-labelledby="additions" />' },
|
||||
{ code: '<div aria-labelledby={"additions"} />' },
|
||||
{ code: '<div aria-labelledby={`additions`} />' },
|
||||
{ code: '<div aria-labelledby="additions removals" />' },
|
||||
{ code: '<div aria-labelledby="additions additions" />' },
|
||||
{ code: '<div aria-labelledby={"additions removals"} />' },
|
||||
{ code: '<div aria-labelledby={`additions removals`} />' },
|
||||
{ code: '<div aria-labelledby="additions removals text" />' },
|
||||
{ code: '<div aria-labelledby={"additions removals text"} />' },
|
||||
{ code: '<div aria-labelledby={`additions removals text`} />' },
|
||||
{ code: '<div aria-labelledby="additions removals text all" />' },
|
||||
{ code: '<div aria-labelledby={"additions removals text all"} />' },
|
||||
{ code: '<div aria-labelledby={`removals additions text all`} />' },
|
||||
{ code: '<div aria-labelledby={foo} />' },
|
||||
{ code: '<div aria-labelledby={foo.bar} />' },
|
||||
{ code: '<div aria-labelledby={null} />' },
|
||||
{ code: '<div aria-labelledby={undefined} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// BOOLEAN
|
||||
{ code: '<div aria-hidden="yes" />', errors: [errorMessage('aria-hidden')] },
|
||||
{ code: '<div aria-hidden="no" />', errors: [errorMessage('aria-hidden')] },
|
||||
{ code: '<div aria-hidden={1234} />', errors: [errorMessage('aria-hidden')] },
|
||||
{
|
||||
code: '<div aria-hidden={`${abc}`} />',
|
||||
errors: [errorMessage('aria-hidden')],
|
||||
},
|
||||
|
||||
// STRING
|
||||
{ code: '<div aria-label />', errors: [errorMessage('aria-label')] },
|
||||
{ code: '<div aria-label={true} />', errors: [errorMessage('aria-label')] },
|
||||
{ code: '<div aria-label={false} />', errors: [errorMessage('aria-label')] },
|
||||
{ code: '<div aria-label={1234} />', errors: [errorMessage('aria-label')] },
|
||||
{ code: '<div aria-label={!true} />', errors: [errorMessage('aria-label')] },
|
||||
|
||||
// TRISTATE
|
||||
{ code: '<div aria-checked="yes" />', errors: [errorMessage('aria-checked')] },
|
||||
{ code: '<div aria-checked="no" />', errors: [errorMessage('aria-checked')] },
|
||||
{ code: '<div aria-checked={1234} />', errors: [errorMessage('aria-checked')] },
|
||||
{
|
||||
code: '<div aria-checked={`${abc}`} />',
|
||||
errors: [errorMessage('aria-checked')],
|
||||
},
|
||||
|
||||
// INTEGER
|
||||
{ code: '<div aria-level="yes" />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level="no" />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level={`abc`} />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level={true} />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level={"false"} />', errors: [errorMessage('aria-level')] },
|
||||
{ code: '<div aria-level={!"false"} />', errors: [errorMessage('aria-level')] },
|
||||
|
||||
// NUMBER
|
||||
{ code: '<div aria-valuemax="yes" />', errors: [errorMessage('aria-valuemax')] },
|
||||
{ code: '<div aria-valuemax="no" />', errors: [errorMessage('aria-valuemax')] },
|
||||
{
|
||||
code: '<div aria-valuemax={`abc`} />',
|
||||
errors: [errorMessage('aria-valuemax')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-valuemax={true} />',
|
||||
errors: [errorMessage('aria-valuemax')],
|
||||
},
|
||||
{ code: '<div aria-valuemax />', errors: [errorMessage('aria-valuemax')] },
|
||||
{
|
||||
code: '<div aria-valuemax={"false"} />',
|
||||
errors: [errorMessage('aria-valuemax')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-valuemax={!"false"} />',
|
||||
errors: [errorMessage('aria-valuemax')],
|
||||
},
|
||||
|
||||
// TOKEN
|
||||
{ code: '<div aria-sort="" />', errors: [errorMessage('aria-sort')] },
|
||||
{ code: '<div aria-sort="descnding" />', errors: [errorMessage('aria-sort')] },
|
||||
{ code: '<div aria-sort />', errors: [errorMessage('aria-sort')] },
|
||||
{ code: '<div aria-sort={true} />', errors: [errorMessage('aria-sort')] },
|
||||
{ code: '<div aria-sort={"false"} />', errors: [errorMessage('aria-sort')] },
|
||||
{
|
||||
code: '<div aria-sort="ascending descending" />',
|
||||
errors: [errorMessage('aria-sort')],
|
||||
},
|
||||
|
||||
// TOKENLIST
|
||||
{ code: '<div aria-relevant="" />', errors: [errorMessage('aria-relevant')] },
|
||||
{
|
||||
code: '<div aria-relevant="foobar" />',
|
||||
errors: [errorMessage('aria-relevant')],
|
||||
},
|
||||
{ code: '<div aria-relevant />', errors: [errorMessage('aria-relevant')] },
|
||||
{
|
||||
code: '<div aria-relevant={true} />',
|
||||
errors: [errorMessage('aria-relevant')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-relevant={"false"} />',
|
||||
errors: [errorMessage('aria-relevant')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-relevant="additions removalss" />',
|
||||
errors: [errorMessage('aria-relevant')],
|
||||
},
|
||||
{
|
||||
code: '<div aria-relevant="additions removalss " />',
|
||||
errors: [errorMessage('aria-relevant')],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-role-test.js
generated
vendored
Normal file
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-role-test.js
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce aria role attribute is valid.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { roles } from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/aria-role';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = {
|
||||
message: 'Elements with ARIA roles must use a valid, non-abstract ARIA role.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
const roleKeys = [...roles.keys()];
|
||||
|
||||
const validRoles = roleKeys.filter((role) => roles.get(role).abstract === false);
|
||||
const invalidRoles = roleKeys.filter((role) => roles.get(role).abstract === true);
|
||||
|
||||
const createTests = (roleNames) => roleNames.map((role) => ({
|
||||
code: `<div role="${role.toLowerCase()}" />`,
|
||||
}));
|
||||
|
||||
const validTests = createTests(validRoles);
|
||||
const invalidTests = createTests(invalidRoles).map((test) => {
|
||||
const invalidTest = { ...test };
|
||||
invalidTest.errors = [errorMessage];
|
||||
return invalidTest;
|
||||
});
|
||||
|
||||
const ignoreNonDOMSchema = [{
|
||||
ignoreNonDOM: true,
|
||||
}];
|
||||
|
||||
ruleTester.run('aria-role', rule, {
|
||||
valid: [
|
||||
// Variables should pass, as we are only testing literals.
|
||||
{ code: '<div />' },
|
||||
{ code: '<div></div>' },
|
||||
{ code: '<div role={role} />' },
|
||||
{ code: '<div role={role || "button"} />' },
|
||||
{ code: '<div role={role || "foobar"} />' },
|
||||
{ code: '<div role="tabpanel row" />' },
|
||||
{ code: '<div role="switch" />' },
|
||||
{ code: '<div role="doc-abstract" />' },
|
||||
{ code: '<div role="doc-appendix doc-bibliography" />' },
|
||||
{ code: '<Bar baz />' },
|
||||
{ code: '<Foo role="bar" />', options: ignoreNonDOMSchema },
|
||||
{ code: '<fakeDOM role="bar" />', options: ignoreNonDOMSchema },
|
||||
{ code: '<img role="presentation" />', options: ignoreNonDOMSchema },
|
||||
].concat(validTests).map(parserOptionsMapper),
|
||||
|
||||
invalid: [
|
||||
{ code: '<div role="foobar" />', errors: [errorMessage] },
|
||||
{ code: '<div role="datepicker"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role="range"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role="Button"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role=""></div>', errors: [errorMessage] },
|
||||
{ code: '<div role="tabpanel row foobar"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role="tabpanel row range"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role="doc-endnotes range"></div>', errors: [errorMessage] },
|
||||
{ code: '<div role />', errors: [errorMessage] },
|
||||
{ code: '<div role={null}></div>', errors: [errorMessage] },
|
||||
{ code: '<Foo role="datepicker" />', errors: [errorMessage] },
|
||||
{ code: '<Foo role="Button" />', errors: [errorMessage] },
|
||||
].concat(invalidTests).map(parserOptionsMapper),
|
||||
});
|
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-unsupported-elements-test.js
generated
vendored
Normal file
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/aria-unsupported-elements-test.js
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce that elements that do not support ARIA roles,
|
||||
* states and properties do not have those attributes.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { dom } from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/aria-unsupported-elements';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = (invalidProp) => ({
|
||||
message: `This element does not support ARIA roles, states and properties. \
|
||||
Try removing the prop '${invalidProp}'.`,
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const domElements = [...dom.keys()];
|
||||
// Generate valid test cases
|
||||
const roleValidityTests = domElements.map((element) => {
|
||||
const isReserved = dom.get(element).reserved || false;
|
||||
const role = isReserved ? '' : 'role';
|
||||
|
||||
return {
|
||||
code: `<${element} ${role} />`,
|
||||
};
|
||||
});
|
||||
|
||||
const ariaValidityTests = domElements.map((element) => {
|
||||
const isReserved = dom.get(element).reserved || false;
|
||||
const aria = isReserved ? '' : 'aria-hidden';
|
||||
|
||||
return {
|
||||
code: `<${element} ${aria} />`,
|
||||
};
|
||||
}).concat({
|
||||
code: '<fake aria-hidden />',
|
||||
errors: [errorMessage('aria-hidden')],
|
||||
});
|
||||
|
||||
// Generate invalid test cases.
|
||||
const invalidRoleValidityTests = domElements
|
||||
.filter((element) => Boolean(dom.get(element).reserved))
|
||||
.map((reservedElem) => ({
|
||||
code: `<${reservedElem} role {...props} />`,
|
||||
errors: [errorMessage('role')],
|
||||
}));
|
||||
|
||||
const invalidAriaValidityTests = domElements
|
||||
.filter((element) => Boolean(dom.get(element).reserved))
|
||||
.map((reservedElem) => ({
|
||||
code: `<${reservedElem} aria-hidden aria-role="none" {...props} />`,
|
||||
errors: [errorMessage('aria-hidden')],
|
||||
}));
|
||||
|
||||
ruleTester.run('aria-unsupported-elements', rule, {
|
||||
valid: roleValidityTests.concat(ariaValidityTests).map(parserOptionsMapper),
|
||||
invalid: invalidRoleValidityTests.concat(invalidAriaValidityTests)
|
||||
.map(parserOptionsMapper),
|
||||
});
|
65
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/autocomplete-valid-test.js
generated
vendored
Normal file
65
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/autocomplete-valid-test.js
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Ensure autocomplete attribute is correct.
|
||||
* @author Wilco Fiers
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import { axeFailMessage } from '../../__util__/axeMapping';
|
||||
import rule from '../../../src/rules/autocomplete-valid';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const invalidAutocomplete = [{
|
||||
message: axeFailMessage('autocomplete-valid'),
|
||||
type: 'JSXOpeningElement',
|
||||
}];
|
||||
|
||||
const inappropriateAutocomplete = [{
|
||||
message: axeFailMessage('autocomplete-appropriate'),
|
||||
type: 'JSXOpeningElement',
|
||||
}];
|
||||
|
||||
ruleTester.run('autocomplete-valid', rule, {
|
||||
valid: [
|
||||
// INAPPLICABLE
|
||||
{ code: '<input type="text" />;' },
|
||||
// // PASSED AUTOCOMPLETE
|
||||
{ code: '<input type="text" autocomplete="name" />;' },
|
||||
{ code: '<input type="text" autocomplete="" />;' },
|
||||
{ code: '<input type="text" autocomplete="off" />;' },
|
||||
{ code: '<input type="text" autocomplete="on" />;' },
|
||||
{ code: '<input type="text" autocomplete="billing family-name" />;' },
|
||||
{ code: '<input type="text" autocomplete="section-blue shipping street-address" />;' },
|
||||
{ code: '<input type="text" autocomplete="section-somewhere shipping work email" />;' },
|
||||
{ code: '<input type="text" autocomplete />;' },
|
||||
{ code: '<input type="text" autocomplete={autocompl} />;' },
|
||||
{ code: '<input type="text" autocomplete={autocompl || "name"} />;' },
|
||||
{ code: '<input type="text" autocomplete={autocompl || "foo"} />;' },
|
||||
{ code: '<Foo autocomplete="bar"></Foo>;' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// FAILED "autocomplete-valid"
|
||||
{ code: '<input type="text" autocomplete="foo" />;', errors: invalidAutocomplete },
|
||||
{ code: '<input type="text" autocomplete="name invalid" />;', errors: invalidAutocomplete },
|
||||
{ code: '<input type="text" autocomplete="invalid name" />;', errors: invalidAutocomplete },
|
||||
{ code: '<input type="text" autocomplete="home url" />;', errors: invalidAutocomplete },
|
||||
{ code: '<Bar autocomplete="baz"></Bar>;', errors: invalidAutocomplete, options: [{ inputComponents: ['Bar'] }] },
|
||||
{ code: '<input type={isEmail ? "email" : "text"} autocomplete="none" />;', errors: invalidAutocomplete },
|
||||
|
||||
// FAILED "autocomplete-appropriate"
|
||||
{ code: '<input type="date" autocomplete="email" />;', errors: inappropriateAutocomplete },
|
||||
{ code: '<input type="number" autocomplete="url" />;', errors: inappropriateAutocomplete },
|
||||
{ code: '<input type="month" autocomplete="tel" />;', errors: inappropriateAutocomplete },
|
||||
{ code: '<Foo type="month" autocomplete="tel"></Foo>;', errors: inappropriateAutocomplete, options: [{ inputComponents: ['Foo'] }] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
75
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/click-events-have-key-events-test.js
generated
vendored
Normal file
75
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/click-events-have-key-events-test.js
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce a clickable non-interactive element has at least 1 keyboard event listener.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/click-events-have-key-events';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = 'Visible, non-interactive elements with click handlers must have at least one keyboard listener.';
|
||||
|
||||
const expectedError = {
|
||||
message: errorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('click-events-have-key-events', rule, {
|
||||
valid: [
|
||||
{ code: '<div onClick={() => void 0} onKeyDown={foo}/>;' },
|
||||
{ code: '<div onClick={() => void 0} onKeyUp={foo} />;' },
|
||||
{ code: '<div onClick={() => void 0} onKeyPress={foo}/>;' },
|
||||
{ code: '<div onClick={() => void 0} onKeyDown={foo} onKeyUp={bar} />;' },
|
||||
{ code: '<div onClick={() => void 0} onKeyDown={foo} {...props} />;' },
|
||||
{ code: '<div className="foo" />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden={true} />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden={false} onKeyDown={foo} />;' },
|
||||
{
|
||||
code: '<div onClick={() => void 0} onKeyDown={foo} aria-hidden={undefined} />;',
|
||||
},
|
||||
{ code: '<input type="text" onClick={() => void 0} />' },
|
||||
{ code: '<input onClick={() => void 0} />' },
|
||||
{ code: '<button onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<option onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<select onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<textarea onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
|
||||
{ code: '<input onClick={() => void 0} type="hidden" />;' },
|
||||
{ code: '<div onClick={() => void 0} role="presentation" />;' },
|
||||
{ code: '<div onClick={() => void 0} role="none" />;' },
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<Button onClick={doFoo} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{
|
||||
code: '<div onClick={() => void 0} role={undefined} />;',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<div onClick={() => void 0} {...props} />;', errors: [expectedError] },
|
||||
{ code: '<section onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<main onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<article onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<header onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<footer onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{
|
||||
code: '<div onClick={() => void 0} aria-hidden={false} />;',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<a onClick={() => void 0} />', errors: [expectedError] },
|
||||
{ code: '<a tabIndex="0" onClick={() => void 0} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
325
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/control-has-associated-label-test.js
generated
vendored
Normal file
325
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/control-has-associated-label-test.js
generated
vendored
Normal file
|
@ -0,0 +1,325 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @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 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: '<CustomControl><span><span>Save</span></span></CustomControl>', options: [{ depth: 3, controlComponents: ['CustomControl'] }] },
|
||||
{ code: '<CustomControl><span><span label="Save"></span></span></CustomControl>', options: [{ depth: 3, controlComponents: ['CustomControl'], labelAttributes: ['label'] }] },
|
||||
// Interactive Elements
|
||||
{ code: '<button>Save</button>' },
|
||||
{ code: '<button><span>Save</span></button>' },
|
||||
{ code: '<button><span><span>Save</span></span></button>', options: [{ depth: 3 }] },
|
||||
{ code: '<button><span><span><span><span><span><span><span><span>Save</span></span></span></span></span></span></span></span></button>', options: [{ depth: 9 }] },
|
||||
{ code: '<button><img alt="Save" /></button>' },
|
||||
{ code: '<button aria-label="Save" />' },
|
||||
{ code: '<button><span aria-label="Save" /></button>' },
|
||||
{ code: '<button aria-labelledby="js_1" />' },
|
||||
{ code: '<button><span aria-labelledby="js_1" /></button>' },
|
||||
{ code: '<button>{sureWhyNot}</button>' },
|
||||
{ code: '<button><span><span label="Save"></span></span></button>', options: [{ depth: 3, labelAttributes: ['label'] }] },
|
||||
{ code: '<a href="#">Save</a>' },
|
||||
{ code: '<area href="#">Save</area>' },
|
||||
{ code: '<link>Save</link>' },
|
||||
{ code: '<menuitem>Save</menuitem>' },
|
||||
{ code: '<option>Save</option>' },
|
||||
{ code: '<th>Save</th>' },
|
||||
// Interactive Roles
|
||||
{ code: '<div role="button">Save</div>' },
|
||||
{ code: '<div role="checkbox">Save</div>' },
|
||||
{ code: '<div role="columnheader">Save</div>' },
|
||||
{ code: '<div role="combobox">Save</div>' },
|
||||
{ code: '<div role="gridcell">Save</div>' },
|
||||
{ code: '<div role="link">Save</div>' },
|
||||
{ code: '<div role="menuitem">Save</div>' },
|
||||
{ code: '<div role="menuitemcheckbox">Save</div>' },
|
||||
{ code: '<div role="menuitemradio">Save</div>' },
|
||||
{ code: '<div role="option">Save</div>' },
|
||||
{ code: '<div role="progressbar">Save</div>' },
|
||||
{ code: '<div role="radio">Save</div>' },
|
||||
{ code: '<div role="rowheader">Save</div>' },
|
||||
{ code: '<div role="searchbox">Save</div>' },
|
||||
{ code: '<div role="slider">Save</div>' },
|
||||
{ code: '<div role="spinbutton">Save</div>' },
|
||||
{ code: '<div role="switch">Save</div>' },
|
||||
{ code: '<div role="tab">Save</div>' },
|
||||
{ code: '<div role="textbox">Save</div>' },
|
||||
{ code: '<div role="treeitem">Save</div>' },
|
||||
{ code: '<div role="button" aria-label="Save" />' },
|
||||
{ code: '<div role="checkbox" aria-label="Save" />' },
|
||||
{ code: '<div role="columnheader" aria-label="Save" />' },
|
||||
{ code: '<div role="combobox" aria-label="Save" />' },
|
||||
{ code: '<div role="gridcell" aria-label="Save" />' },
|
||||
{ code: '<div role="link" aria-label="Save" />' },
|
||||
{ code: '<div role="menuitem" aria-label="Save" />' },
|
||||
{ code: '<div role="menuitemcheckbox" aria-label="Save" />' },
|
||||
{ code: '<div role="menuitemradio" aria-label="Save" />' },
|
||||
{ code: '<div role="option" aria-label="Save" />' },
|
||||
{ code: '<div role="progressbar" aria-label="Save" />' },
|
||||
{ code: '<div role="radio" aria-label="Save" />' },
|
||||
{ code: '<div role="rowheader" aria-label="Save" />' },
|
||||
{ code: '<div role="searchbox" aria-label="Save" />' },
|
||||
{ code: '<div role="slider" aria-label="Save" />' },
|
||||
{ code: '<div role="spinbutton" aria-label="Save" />' },
|
||||
{ code: '<div role="switch" aria-label="Save" />' },
|
||||
{ code: '<div role="tab" aria-label="Save" />' },
|
||||
{ code: '<div role="textbox" aria-label="Save" />' },
|
||||
{ code: '<div role="treeitem" aria-label="Save" />' },
|
||||
{ code: '<div role="button" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="checkbox" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="columnheader" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="combobox" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="gridcell" aria-labelledby="Save" />' },
|
||||
{ code: '<div role="link" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="menuitem" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="menuitemcheckbox" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="menuitemradio" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="option" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="progressbar" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="radio" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="rowheader" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="searchbox" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="slider" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="spinbutton" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="switch" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="tab" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="textbox" aria-labelledby="js_1" />' },
|
||||
{ code: '<div role="treeitem" aria-labelledby="js_1" />' },
|
||||
// Non-interactive Elements
|
||||
{ code: '<abbr />' },
|
||||
{ code: '<article />' },
|
||||
{ code: '<blockquote />' },
|
||||
{ code: '<br />' },
|
||||
{ code: '<caption />' },
|
||||
{ code: '<dd />' },
|
||||
{ code: '<details />' },
|
||||
{ code: '<dfn />' },
|
||||
{ code: '<dialog />' },
|
||||
{ code: '<dir />' },
|
||||
{ code: '<dl />' },
|
||||
{ code: '<dt />' },
|
||||
{ code: '<fieldset />' },
|
||||
{ code: '<figcaption />' },
|
||||
{ code: '<figure />' },
|
||||
{ code: '<footer />' },
|
||||
{ code: '<form />' },
|
||||
{ code: '<frame />' },
|
||||
{ code: '<h1 />' },
|
||||
{ code: '<h2 />' },
|
||||
{ code: '<h3 />' },
|
||||
{ code: '<h4 />' },
|
||||
{ code: '<h5 />' },
|
||||
{ code: '<h6 />' },
|
||||
{ code: '<hr />' },
|
||||
{ code: '<iframe />' },
|
||||
{ code: '<img />' },
|
||||
{ code: '<label />' },
|
||||
{ code: '<legend />' },
|
||||
{ code: '<li />' },
|
||||
{ code: '<link />' },
|
||||
{ code: '<main />' },
|
||||
{ code: '<mark />' },
|
||||
{ code: '<marquee />' },
|
||||
{ code: '<menu />' },
|
||||
{ code: '<meter />' },
|
||||
{ code: '<nav />' },
|
||||
{ code: '<ol />' },
|
||||
{ code: '<p />' },
|
||||
{ code: '<pre />' },
|
||||
{ code: '<progress />' },
|
||||
{ code: '<ruby />' },
|
||||
{ code: '<section />' },
|
||||
{ code: '<table />' },
|
||||
{ code: '<tbody />' },
|
||||
{ code: '<td />' },
|
||||
{ code: '<tfoot />' },
|
||||
{ code: '<thead />' },
|
||||
{ code: '<time />' },
|
||||
{ code: '<ul />' },
|
||||
// Non-interactive Roles
|
||||
{ code: '<div role="alert" />' },
|
||||
{ code: '<div role="alertdialog" />' },
|
||||
{ code: '<div role="application" />' },
|
||||
{ code: '<div role="article" />' },
|
||||
{ code: '<div role="banner" />' },
|
||||
{ code: '<div role="cell" />' },
|
||||
{ code: '<div role="complementary" />' },
|
||||
{ code: '<div role="contentinfo" />' },
|
||||
{ code: '<div role="definition" />' },
|
||||
{ code: '<div role="dialog" />' },
|
||||
{ code: '<div role="directory" />' },
|
||||
{ code: '<div role="document" />' },
|
||||
{ code: '<div role="feed" />' },
|
||||
{ code: '<div role="figure" />' },
|
||||
{ code: '<div role="form" />' },
|
||||
{ code: '<div role="group" />' },
|
||||
{ code: '<div role="heading" />' },
|
||||
{ code: '<div role="img" />' },
|
||||
{ code: '<div role="list" />' },
|
||||
{ code: '<div role="listitem" />' },
|
||||
{ code: '<div role="log" />' },
|
||||
{ code: '<div role="main" />' },
|
||||
{ code: '<div role="marquee" />' },
|
||||
{ code: '<div role="math" />' },
|
||||
{ code: '<div role="navigation" />' },
|
||||
{ code: '<div role="none" />' },
|
||||
{ code: '<div role="note" />' },
|
||||
{ code: '<div role="presentation" />' },
|
||||
{ code: '<div role="region" />' },
|
||||
{ code: '<div role="rowgroup" />' },
|
||||
{ code: '<div role="search" />' },
|
||||
{ code: '<div role="separator" />' },
|
||||
{ code: '<div role="status" />' },
|
||||
{ code: '<div role="table" />' },
|
||||
{ code: '<div role="tabpanel" />' },
|
||||
{ code: '<div role="term" />' },
|
||||
{ code: '<div role="timer" />' },
|
||||
{ code: '<div role="tooltip" />' },
|
||||
// Via config
|
||||
// Inputs. Ignore them because they might get a label from a wrapping label element.
|
||||
{ code: '<input />' },
|
||||
{ code: '<input type="button" />' },
|
||||
{ code: '<input type="checkbox" />' },
|
||||
{ code: '<input type="color" />' },
|
||||
{ code: '<input type="date" />' },
|
||||
{ code: '<input type="datetime" />' },
|
||||
{ code: '<input type="email" />' },
|
||||
{ code: '<input type="file" />' },
|
||||
{ code: '<input type="hidden" />' },
|
||||
{ code: '<input type="hidden" name="bot-field"/>' },
|
||||
{ code: '<input type="hidden" name="form-name" value="Contact Form"/>' },
|
||||
{ code: '<input type="image" />' },
|
||||
{ code: '<input type="month" />' },
|
||||
{ code: '<input type="number" />' },
|
||||
{ code: '<input type="password" />' },
|
||||
{ code: '<input type="radio" />' },
|
||||
{ code: '<input type="range" />' },
|
||||
{ code: '<input type="reset" />' },
|
||||
{ code: '<input type="search" />' },
|
||||
{ code: '<input type="submit" />' },
|
||||
{ code: '<input type="tel" />' },
|
||||
{ code: '<input type="text" />' },
|
||||
{ code: '<label>Foo <input type="text" /></label>' },
|
||||
{ code: '<input name={field.name} id="foo" type="text" value={field.value} disabled={isDisabled} onChange={changeText(field.onChange, field.name)} onBlur={field.onBlur} />' },
|
||||
{ code: '<input type="time" />' },
|
||||
{ code: '<input type="url" />' },
|
||||
{ code: '<input type="week" />' },
|
||||
// Marginal interactive elements. It is difficult to insist that these
|
||||
// elements contain a text label.
|
||||
{ code: '<audio />' },
|
||||
{ code: '<canvas />' },
|
||||
{ code: '<embed />' },
|
||||
{ code: '<textarea />' },
|
||||
{ code: '<tr />' },
|
||||
{ code: '<video />' },
|
||||
// Interactive roles to ignore
|
||||
{ code: '<div role="grid" />' },
|
||||
{ code: '<div role="listbox" />' },
|
||||
{ code: '<div role="menu" />' },
|
||||
{ code: '<div role="menubar" />' },
|
||||
{ code: '<div role="radiogroup" />' },
|
||||
{ code: '<div role="row" />' },
|
||||
{ code: '<div role="tablist" />' },
|
||||
{ code: '<div role="toolbar" />' },
|
||||
{ code: '<div role="tree" />' },
|
||||
{ code: '<div role="treegrid" />' },
|
||||
];
|
||||
const neverValid = [
|
||||
{ code: '<button />', errors: [expectedError] },
|
||||
{ code: '<button><span /></button>', errors: [expectedError] },
|
||||
{ code: '<button><img /></button>', errors: [expectedError] },
|
||||
{ code: '<button><span title="This is not a real label" /></button>', errors: [expectedError] },
|
||||
{ code: '<button><span><span><span>Save</span></span></span></button>', options: [{ depth: 3 }], errors: [expectedError] },
|
||||
{ code: '<CustomControl><span><span></span></span></CustomControl>', options: [{ depth: 3, controlComponents: ['CustomControl'] }], errors: [expectedError] },
|
||||
{ code: '<a href="#" />', errors: [expectedError] },
|
||||
{ code: '<area href="#" />', errors: [expectedError] },
|
||||
{ code: '<menuitem />', errors: [expectedError] },
|
||||
{ code: '<option />', errors: [expectedError] },
|
||||
{ code: '<th />', errors: [expectedError] },
|
||||
// Interactive Roles
|
||||
{ code: '<div role="button" />', errors: [expectedError] },
|
||||
{ code: '<div role="checkbox" />', errors: [expectedError] },
|
||||
{ code: '<div role="columnheader" />', errors: [expectedError] },
|
||||
{ code: '<div role="combobox" />', errors: [expectedError] },
|
||||
{ code: '<div role="link" />', errors: [expectedError] },
|
||||
{ code: '<div role="gridcell" />', errors: [expectedError] },
|
||||
{ code: '<div role="menuitem" />', errors: [expectedError] },
|
||||
{ code: '<div role="menuitemcheckbox" />', errors: [expectedError] },
|
||||
{ code: '<div role="menuitemradio" />', errors: [expectedError] },
|
||||
{ code: '<div role="option" />', errors: [expectedError] },
|
||||
{ code: '<div role="progressbar" />', errors: [expectedError] },
|
||||
{ code: '<div role="radio" />', errors: [expectedError] },
|
||||
{ code: '<div role="rowheader" />', errors: [expectedError] },
|
||||
{ code: '<div role="scrollbar" />', errors: [expectedError] },
|
||||
{ code: '<div role="searchbox" />', errors: [expectedError] },
|
||||
{ code: '<div role="slider" />', errors: [expectedError] },
|
||||
{ code: '<div role="spinbutton" />', errors: [expectedError] },
|
||||
{ code: '<div role="switch" />', errors: [expectedError] },
|
||||
{ code: '<div role="tab" />', errors: [expectedError] },
|
||||
{ code: '<div role="textbox" />', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
const strictOptions = (configs.strict.rules[ruleName][1] || {});
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:no-config`, rule, {
|
||||
valid: [
|
||||
{ code: '<input type="hidden" />' },
|
||||
{ code: '<input type="text" aria-hidden="true" />' },
|
||||
]
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<input type="text" />', errors: [expectedError] },
|
||||
]
|
||||
.map(parserOptionsMapper),
|
||||
});
|
67
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/heading-has-content-test.js
generated
vendored
Normal file
67
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/heading-has-content-test.js
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce heading (h1, h2, etc) elements contain accessible content.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/heading-has-content';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Headings must have content and the content must be accessible by a screen reader.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const components = [{
|
||||
components: ['Heading', 'Title'],
|
||||
}];
|
||||
|
||||
ruleTester.run('heading-has-content', rule, {
|
||||
valid: [
|
||||
// DEFAULT ELEMENT TESTS
|
||||
{ code: '<div />;' },
|
||||
{ code: '<h1>Foo</h1>' },
|
||||
{ code: '<h2>Foo</h2>' },
|
||||
{ code: '<h3>Foo</h3>' },
|
||||
{ code: '<h4>Foo</h4>' },
|
||||
{ code: '<h5>Foo</h5>' },
|
||||
{ code: '<h6>Foo</h6>' },
|
||||
{ code: '<h6>123</h6>' },
|
||||
{ code: '<h1><Bar /></h1>' },
|
||||
{ code: '<h1>{foo}</h1>' },
|
||||
{ code: '<h1>{foo.bar}</h1>' },
|
||||
{ code: '<h1 dangerouslySetInnerHTML={{ __html: "foo" }} />' },
|
||||
{ code: '<h1 children={children} />' },
|
||||
// CUSTOM ELEMENT TESTS FOR COMPONENTS OPTION
|
||||
{ code: '<Heading>Foo</Heading>', options: components },
|
||||
{ code: '<Title>Foo</Title>', options: components },
|
||||
{ code: '<Heading><Bar /></Heading>', options: components },
|
||||
{ code: '<Heading>{foo}</Heading>', options: components },
|
||||
{ code: '<Heading>{foo.bar}</Heading>', options: components },
|
||||
{ code: '<Heading dangerouslySetInnerHTML={{ __html: "foo" }} />', options: components },
|
||||
{ code: '<Heading children={children} />', options: components },
|
||||
{ code: '<h1 aria-hidden />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// DEFAULT ELEMENT TESTS
|
||||
{ code: '<h1 />', errors: [expectedError] },
|
||||
{ code: '<h1><Bar aria-hidden /></h1>', errors: [expectedError] },
|
||||
{ code: '<h1>{undefined}</h1>', errors: [expectedError] },
|
||||
|
||||
// CUSTOM ELEMENT TESTS FOR COMPONENTS OPTION
|
||||
{ code: '<Heading />', errors: [expectedError], options: components },
|
||||
{ code: '<Heading><Bar aria-hidden /></Heading>', errors: [expectedError], options: components },
|
||||
{ code: '<Heading>{undefined}</Heading>', errors: [expectedError], options: components },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
40
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/html-has-lang-test.js
generated
vendored
Normal file
40
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/html-has-lang-test.js
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce html element has lang prop.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/html-has-lang';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: '<html> elements must have the lang prop.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('html-has-lang', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<html lang="en" />' },
|
||||
{ code: '<html lang="en-US" />' },
|
||||
{ code: '<html lang={foo} />' },
|
||||
{ code: '<html lang />' },
|
||||
{ code: '<HTML />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<html />', errors: [expectedError] },
|
||||
{ code: '<html {...props} />', errors: [expectedError] },
|
||||
{ code: '<html lang={undefined} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/iframe-has-title-test.js
generated
vendored
Normal file
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/iframe-has-title-test.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce iframe elements have a title attribute.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/iframe-has-title';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: '<iframe> elements must have a unique title property.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('html-has-lang', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<iframe title="Unique title" />' },
|
||||
{ code: '<iframe title={foo} />' },
|
||||
{ code: '<FooComponent />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<iframe />', errors: [expectedError] },
|
||||
{ code: '<iframe {...props} />', errors: [expectedError] },
|
||||
{ code: '<iframe title={undefined} />', errors: [expectedError] },
|
||||
{ code: '<iframe title="" />', errors: [expectedError] },
|
||||
{ code: '<iframe title={false} />', errors: [expectedError] },
|
||||
{ code: '<iframe title={true} />', errors: [expectedError] },
|
||||
{ code: "<iframe title={''} />", errors: [expectedError] },
|
||||
{ code: '<iframe title={``} />', errors: [expectedError] },
|
||||
{ code: '<iframe title={""} />', errors: [expectedError] },
|
||||
{ code: '<iframe title={42} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
115
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/img-redundant-alt-test.js
generated
vendored
Normal file
115
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/img-redundant-alt-test.js
generated
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce img alt attribute does not have the word image, picture, or photo.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/img-redundant-alt';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const array = [{
|
||||
components: ['Image'],
|
||||
words: ['Word1', 'Word2'],
|
||||
}];
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Redundant alt attribute. Screen-readers already announce `img` tags as an image. You don’t need to use the words `image`, `photo,` or `picture` (or any specified custom words) in the alt prop.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('img-redundant-alt', rule, {
|
||||
valid: [
|
||||
{ code: '<img alt="foo" />;' },
|
||||
{ code: '<img alt="picture of me taking a photo of an image" aria-hidden />' },
|
||||
{ code: '<img aria-hidden alt="photo of image" />' },
|
||||
{ code: '<img ALt="foo" />;' },
|
||||
{ code: '<img {...this.props} alt="foo" />' },
|
||||
{ code: '<img {...this.props} alt={"foo"} />' },
|
||||
{ code: '<img {...this.props} alt={alt} />' },
|
||||
{ code: '<a />' },
|
||||
{ code: '<img />' },
|
||||
{ code: '<IMG />' },
|
||||
{ code: '<img alt={undefined} />' },
|
||||
{ code: '<img alt={`this should pass for ${now}`} />' },
|
||||
{ code: '<img alt={`this should pass for ${photo}`} />' },
|
||||
{ code: '<img alt={`this should pass for ${image}`} />' },
|
||||
{ code: '<img alt={`this should pass for ${picture}`} />' },
|
||||
{ code: '<img alt={`${photo}`} />' },
|
||||
{ code: '<img alt={`${image}`} />' },
|
||||
{ code: '<img alt={`${picture}`} />' },
|
||||
{ code: '<img alt={"undefined"} />' },
|
||||
{ code: '<img alt={() => {}} />' },
|
||||
{ code: '<img alt={function(e){}} />' },
|
||||
{ code: '<img aria-hidden={false} alt="Doing cool things." />' },
|
||||
{ code: '<UX.Layout>test</UX.Layout>' },
|
||||
{ code: '<img alt={imageAlt} />' },
|
||||
{ code: '<img alt />' },
|
||||
{ code: '<img alt="Photography" />;' },
|
||||
{ code: '<img alt="ImageMagick" />;' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<img alt="Photo of friend." />;', errors: [expectedError] },
|
||||
{ code: '<img alt="Picture of friend." />;', errors: [expectedError] },
|
||||
{ code: '<img alt="Image of friend." />;', errors: [expectedError] },
|
||||
{ code: '<img alt="PhOtO of friend." />;', errors: [expectedError] },
|
||||
{ code: '<img alt={"photo"} />;', errors: [expectedError] },
|
||||
{ code: '<img alt="piCTUre of friend." />;', errors: [expectedError] },
|
||||
{ code: '<img alt="imAGE of friend." />;', errors: [expectedError] },
|
||||
{
|
||||
code: '<img alt="photo of cool person" aria-hidden={false} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt="picture of cool person" aria-hidden={false} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt="image of cool person" aria-hidden={false} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<img alt="photo" {...this.props} />', errors: [expectedError] },
|
||||
{ code: '<img alt="image" {...this.props} />', errors: [expectedError] },
|
||||
{ code: '<img alt="picture" {...this.props} />', errors: [expectedError] },
|
||||
{
|
||||
code: '<img alt={`picture doing ${things}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt={`photo doing ${things}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt={`image doing ${things}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt={`picture doing ${picture}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt={`photo doing ${photo}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<img alt={`image doing ${image}`} {...this.props} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
|
||||
// TESTS FOR ARRAY OPTION TESTS
|
||||
{ code: '<img alt="Word1" />;', options: array, errors: [expectedError] },
|
||||
{ code: '<img alt="Word2" />;', options: array, errors: [expectedError] },
|
||||
{ code: '<Image alt="Word1" />;', options: array, errors: [expectedError] },
|
||||
{ code: '<Image alt="Word2" />;', options: array, errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
250
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/interactive-supports-focus-test.js
generated
vendored
Normal file
250
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/interactive-supports-focus-test.js
generated
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce that elements with onClick handlers must be focusable.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import includes from 'array-includes';
|
||||
import { RuleTester } from 'eslint';
|
||||
import {
|
||||
eventHandlers,
|
||||
eventHandlersByType,
|
||||
} from 'jsx-ast-utils';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/interactive-supports-focus';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
function template(strings, ...keys) {
|
||||
return (...values) => keys.reduce(
|
||||
(acc, k, i) => acc + (values[k] || '') + strings[i + 1],
|
||||
strings[0],
|
||||
);
|
||||
}
|
||||
|
||||
const ruleName = 'interactive-supports-focus';
|
||||
const type = 'JSXOpeningElement';
|
||||
const codeTemplate = template`<${0} role="${1}" ${2}={() => void 0} />`;
|
||||
const tabindexTemplate = template`<${0} role="${1}" ${2}={() => void 0} tabIndex="0" />`;
|
||||
const tabbableTemplate = template`Elements with the '${0}' interactive role must be tabbable.`;
|
||||
const focusableTemplate = template`Elements with the '${0}' interactive role must be focusable.`;
|
||||
|
||||
const recommendedOptions = configs.recommended.rules[`jsx-a11y/${ruleName}`][1] || {};
|
||||
|
||||
const strictOptions = configs.strict.rules[`jsx-a11y/${ruleName}`][1] || {};
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<div />' },
|
||||
{ code: '<div aria-hidden onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={true == true} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={true === true} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={hidden !== false} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={hidden != false} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={1 < 2} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={1 <= 2} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={2 > 1} onClick={() => void 0} />' },
|
||||
{ code: '<div aria-hidden={2 >= 1} onClick={() => void 0} />' },
|
||||
{ code: '<div onClick={() => void 0} />;' },
|
||||
{ code: '<div onClick={() => void 0} tabIndex={undefined} />;' },
|
||||
{ code: '<div onClick={() => void 0} tabIndex="bad" />;' },
|
||||
{ code: '<div onClick={() => void 0} role={undefined} />;' },
|
||||
{ code: '<div role="section" onClick={() => void 0} />' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden={false} />;' },
|
||||
{ code: '<div onClick={() => void 0} {...props} />;' },
|
||||
{ code: '<input type="text" onClick={() => void 0} />' },
|
||||
{ code: '<input type="hidden" onClick={() => void 0} tabIndex="-1" />' },
|
||||
{ code: '<input type="hidden" onClick={() => void 0} tabIndex={-1} />' },
|
||||
{ code: '<input onClick={() => void 0} />' },
|
||||
{ code: '<input onClick={() => void 0} role="combobox" />' },
|
||||
{ code: '<button onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<option onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<select onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<area href="#" onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<area onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<summary onClick={() => void 0} />' },
|
||||
{ code: '<textarea onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<a onClick="showNextPage();">Next page</a>' },
|
||||
{ code: '<a onClick="showNextPage();" tabIndex={undefined}>Next page</a>' },
|
||||
{ code: '<a onClick="showNextPage();" tabIndex="bad">Next page</a>' },
|
||||
{ code: '<a onClick={() => void 0} />' },
|
||||
{ code: '<a tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<a tabIndex={dynamicTabIndex} onClick={() => void 0} />' },
|
||||
{ code: '<a tabIndex={0} onClick={() => void 0} />' },
|
||||
{ code: '<a role="button" href="#" onClick={() => void 0} />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex={0} />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" role="button" />' },
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<input onClick={() => void 0} type="hidden" />;' },
|
||||
{ code: '<span onClick="submitForm();">Submit</span>' },
|
||||
{ code: '<span onClick="submitForm();" tabIndex={undefined}>Submit</span>' },
|
||||
{ code: '<span onClick="submitForm();" tabIndex="bad">Submit</span>' },
|
||||
{ code: '<span onClick="doSomething();" tabIndex="0">Click me!</span>' },
|
||||
{ code: '<span onClick="doSomething();" tabIndex={0}>Click me!</span>' },
|
||||
{ code: '<span onClick="doSomething();" tabIndex="-1">Click me too!</span>' },
|
||||
{
|
||||
code: '<a href="javascript:void(0);" onClick="doSomething();">Click ALL the things!</a>',
|
||||
},
|
||||
{ code: '<section onClick={() => void 0} />;' },
|
||||
{ code: '<main onClick={() => void 0} />;' },
|
||||
{ code: '<article onClick={() => void 0} />;' },
|
||||
{ code: '<header onClick={() => void 0} />;' },
|
||||
{ code: '<footer onClick={() => void 0} />;' },
|
||||
{ code: '<div role="button" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="checkbox" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="link" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="menuitem" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="menuitemcheckbox" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="menuitemradio" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="option" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="radio" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="spinbutton" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="switch" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="tablist" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="tab" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="textbox" tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<div role="textbox" aria-disabled="true" onClick={() => void 0} />' },
|
||||
{ code: '<Foo.Bar onClick={() => void 0} aria-hidden={false} />;' },
|
||||
{ code: '<Input onClick={() => void 0} type="hidden" />;' },
|
||||
];
|
||||
|
||||
const interactiveRoles = [
|
||||
'button',
|
||||
'checkbox',
|
||||
'link',
|
||||
'gridcell',
|
||||
'menuitem',
|
||||
'menuitemcheckbox',
|
||||
'menuitemradio',
|
||||
'option',
|
||||
'radio',
|
||||
'searchbox',
|
||||
'slider',
|
||||
'spinbutton',
|
||||
'switch',
|
||||
'tab',
|
||||
'textbox',
|
||||
'treeitem',
|
||||
];
|
||||
|
||||
const recommendedRoles = [
|
||||
'button',
|
||||
'checkbox',
|
||||
'link',
|
||||
'searchbox',
|
||||
'spinbutton',
|
||||
'switch',
|
||||
'textbox',
|
||||
];
|
||||
|
||||
const strictRoles = [
|
||||
'button',
|
||||
'checkbox',
|
||||
'link',
|
||||
'progressbar',
|
||||
'searchbox',
|
||||
'slider',
|
||||
'spinbutton',
|
||||
'switch',
|
||||
'textbox',
|
||||
];
|
||||
|
||||
const staticElements = [
|
||||
'div',
|
||||
];
|
||||
|
||||
const triggeringHandlers = [
|
||||
...eventHandlersByType.mouse,
|
||||
...eventHandlersByType.keyboard,
|
||||
];
|
||||
|
||||
const passReducer = (roles, handlers, messageTemplate) => (
|
||||
staticElements.reduce((elementAcc, element) => (
|
||||
elementAcc.concat(roles.reduce((roleAcc, role) => (
|
||||
roleAcc.concat(handlers.map((handler) => ({
|
||||
code: messageTemplate(element, role, handler),
|
||||
})))
|
||||
), []))
|
||||
), [])
|
||||
);
|
||||
|
||||
const failReducer = (roles, handlers, messageTemplate) => (
|
||||
staticElements.reduce((elementAcc, element) => (
|
||||
elementAcc.concat(roles.reduce((roleAcc, role) => (
|
||||
roleAcc.concat(handlers.map((handler) => ({
|
||||
code: codeTemplate(element, role, handler),
|
||||
errors: [{
|
||||
type,
|
||||
message: messageTemplate(role),
|
||||
}],
|
||||
})))
|
||||
), []))
|
||||
), [])
|
||||
);
|
||||
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
...passReducer(
|
||||
interactiveRoles,
|
||||
eventHandlers.filter((handler) => !includes(triggeringHandlers, handler)),
|
||||
codeTemplate,
|
||||
),
|
||||
...passReducer(
|
||||
interactiveRoles.filter((role) => !includes(recommendedRoles, role)),
|
||||
eventHandlers.filter((handler) => includes(triggeringHandlers, handler)),
|
||||
tabindexTemplate,
|
||||
),
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...failReducer(recommendedRoles, triggeringHandlers, tabbableTemplate),
|
||||
...failReducer(
|
||||
interactiveRoles.filter((role) => !includes(recommendedRoles, role)),
|
||||
triggeringHandlers,
|
||||
focusableTemplate,
|
||||
),
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
...passReducer(
|
||||
interactiveRoles,
|
||||
eventHandlers.filter((handler) => !includes(triggeringHandlers, handler)),
|
||||
codeTemplate,
|
||||
),
|
||||
...passReducer(
|
||||
interactiveRoles.filter((role) => !includes(strictRoles, role)),
|
||||
eventHandlers.filter((handler) => includes(triggeringHandlers, handler)),
|
||||
tabindexTemplate,
|
||||
),
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...failReducer(strictRoles, triggeringHandlers, tabbableTemplate),
|
||||
...failReducer(
|
||||
interactiveRoles.filter((role) => !includes(strictRoles, role)),
|
||||
triggeringHandlers,
|
||||
focusableTemplate,
|
||||
),
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
195
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/label-has-associated-control-test.js
generated
vendored
Normal file
195
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/label-has-associated-control-test.js
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @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 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'] }] },
|
||||
// Custom label attributes.
|
||||
{ code: '<label htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['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: '<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'] }] },
|
||||
];
|
||||
|
||||
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'] }] },
|
||||
// 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] },
|
||||
// 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] },
|
||||
];
|
||||
|
||||
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] },
|
||||
// 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] },
|
||||
];
|
||||
// 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),
|
||||
});
|
223
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/label-has-for-test.js
generated
vendored
Normal file
223
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/label-has-for-test.js
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce label tags have htmlFor attribute.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import assign from 'object.assign';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/label-has-for';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedNestingError = {
|
||||
message: 'Form label must have the following type of associated control: nesting',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const expectedSomeError = {
|
||||
message: 'Form label must have ANY of the following types of associated control: nesting, id',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const expectedEveryError = {
|
||||
message: 'Form label must have ALL of the following types of associated control: nesting, id',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const optionsComponents = [{
|
||||
components: ['Label', 'Descriptor'],
|
||||
}];
|
||||
const optionsRequiredNesting = [{
|
||||
required: 'nesting',
|
||||
}];
|
||||
const optionsRequiredSome = [{
|
||||
required: { some: ['nesting', 'id'] },
|
||||
}];
|
||||
const optionsRequiredEvery = [{
|
||||
required: { every: ['nesting', 'id'] },
|
||||
}];
|
||||
const optionsChildrenAllowed = [{
|
||||
allowChildren: true,
|
||||
}];
|
||||
|
||||
ruleTester.run('label-has-for', rule, {
|
||||
valid: [
|
||||
// DEFAULT ELEMENT 'label' TESTS
|
||||
{ code: '<div />' },
|
||||
{ code: '<label htmlFor="foo"><input /></label>' },
|
||||
{ code: '<label htmlFor="foo"><textarea /></label>' },
|
||||
{ code: '<Label />' }, // lower-case convention refers to real HTML elements.
|
||||
{ code: '<Label htmlFor="foo" />' },
|
||||
{ code: '<Descriptor />' },
|
||||
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>' },
|
||||
{ code: '<UX.Layout>test</UX.Layout>' },
|
||||
|
||||
// CUSTOM ELEMENT ARRAY OPTION TESTS
|
||||
{ code: '<Label htmlFor="foo" />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Label htmlFor={"foo"} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Label htmlFor={foo} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Label htmlFor={`${id}`} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<div />', options: optionsComponents },
|
||||
{ code: '<Label htmlFor="something"><input /></Label>', options: optionsComponents },
|
||||
{ code: '<Label htmlFor="foo">Test!</Label>', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Descriptor htmlFor="foo" />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Descriptor htmlFor={"foo"} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Descriptor htmlFor={foo} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Descriptor htmlFor={`${id}`} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
|
||||
{ code: '<label htmlFor="foo" />', options: optionsRequiredSome },
|
||||
{ code: '<label htmlFor={"foo"} />', options: optionsRequiredSome },
|
||||
{ code: '<label htmlFor={foo} />', options: optionsRequiredSome },
|
||||
{ code: '<label htmlFor={`${id}`} />', options: optionsRequiredSome },
|
||||
{ code: '<label htmlFor="foo">Test!</label>', options: optionsRequiredSome },
|
||||
{ code: '<label><input /></label>', options: optionsRequiredSome },
|
||||
{ code: '<label><input /></label>', options: optionsRequiredNesting },
|
||||
{ code: '<label htmlFor="input"><input /></label>', options: optionsRequiredEvery },
|
||||
{ code: '<label><input /></label>', options: optionsChildrenAllowed },
|
||||
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, optionsComponents, optionsChildrenAllowed)] },
|
||||
{ code: '<label>Test!</label>', options: optionsChildrenAllowed },
|
||||
{ code: '<label htmlFor="foo">Test!</label>', options: optionsChildrenAllowed },
|
||||
{ code: '<label>{children}</label>', options: optionsChildrenAllowed },
|
||||
{ code: '<label htmlFor="children">{children}</label>', options: optionsChildrenAllowed },
|
||||
{ code: '<label htmlFor={id}>{ labelText }<div><input id={id} type="checkbox" name={id} value={value} /></div></label>', options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={id}>{ labelText }<div><div><div><div><input id={id} type="checkbox" name={id} value={value} /></div></div></div></div></label>', options: optionsRequiredEvery },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
// DEFAULT ELEMENT 'label' TESTS
|
||||
{ code: '<label id="foo" />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={undefined} />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={`${undefined}`} />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label>First Name</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label {...props}>Foo</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label><input /></label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label><textarea /></label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label>{children}</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor="foo" />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={"foo"} />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={foo} />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={`${id}`} />', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor="foo">Test!</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
{ code: '<label htmlFor={id}>{ labelText }<div><div><div><div><div id={id} type="checkbox" name={id} value={value} /></div></div></div></div></label>', errors: [expectedEveryError], options: optionsRequiredEvery },
|
||||
//
|
||||
// // CUSTOM ELEMENT ARRAY OPTION TESTS
|
||||
{
|
||||
code: '<Label></Label>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor="foo" />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor={"foo"} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor={foo} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor={`${id}`} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor="foo">Test!</Label>',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor="foo" />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor={"foo"} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor={foo} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor={`${id}`} />',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor="foo">Test!</Descriptor>',
|
||||
errors: [expectedEveryError],
|
||||
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
|
||||
},
|
||||
{ code: '<Label id="foo" />', errors: [expectedEveryError], options: optionsComponents },
|
||||
{
|
||||
code: '<Label htmlFor={undefined} />',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{
|
||||
code: '<Label htmlFor={`${undefined}`} />',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{ code: '<Label>First Name</Label>', errors: [expectedEveryError], options: optionsComponents },
|
||||
{
|
||||
code: '<Label {...props}>Foo</Label>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{ code: '<Descriptor id="foo" />', errors: [expectedEveryError], options: optionsComponents },
|
||||
{
|
||||
code: '<Descriptor htmlFor={undefined} />',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{
|
||||
code: '<Descriptor htmlFor={`${undefined}`} />',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{
|
||||
code: '<Descriptor>First Name</Descriptor>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{
|
||||
code: '<Descriptor {...props}>Foo</Descriptor>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsComponents,
|
||||
},
|
||||
{ code: '<label>{children}</label>', errors: [expectedEveryError], options: optionsComponents },
|
||||
{ code: '<label htmlFor="foo" />', errors: [expectedNestingError], options: optionsRequiredNesting },
|
||||
{ code: '<label>First Name</label>', errors: [expectedNestingError], options: optionsRequiredNesting },
|
||||
{ code: '<label>First Name</label>', errors: [expectedSomeError], options: optionsRequiredSome },
|
||||
{ code: '<label>{children}</label>', errors: [expectedSomeError], options: optionsRequiredSome },
|
||||
{ code: '<label>{children}</label>', errors: [expectedNestingError], options: optionsRequiredNesting },
|
||||
{
|
||||
code: '<form><input type="text" id="howmuch" value="1" /><label htmlFor="howmuch">How much ?</label></form>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsRequiredEvery,
|
||||
},
|
||||
{
|
||||
code: '<form><input type="text" id="howmuch" value="1" /><label htmlFor="howmuch">How much ?<span /></label></form>',
|
||||
errors: [expectedEveryError],
|
||||
options: optionsRequiredEvery,
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
46
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/lang-test.js
generated
vendored
Normal file
46
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/lang-test.js
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce lang attribute has a valid value.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/lang';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'lang attribute must have a valid value.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
ruleTester.run('lang', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div foo="bar" />;' },
|
||||
{ code: '<div lang="foo" />;' },
|
||||
{ code: '<html lang="en" />' },
|
||||
{ code: '<html lang="en-US" />' },
|
||||
{ code: '<html lang="zh-Hans" />' },
|
||||
{ code: '<html lang="zh-Hant-HK" />' },
|
||||
{ code: '<html lang="zh-yue-Hant" />' },
|
||||
{ code: '<html lang="ja-Latn" />' },
|
||||
{ code: '<html lang={foo} />' },
|
||||
{ code: '<HTML lang="foo" />' },
|
||||
{ code: '<Foo lang="bar" />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<html lang="foo" />', errors: [expectedError] },
|
||||
{ code: '<html lang="zz-LL" />', errors: [expectedError] },
|
||||
{ code: '<html lang={undefined} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
138
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/media-has-caption-test.js
generated
vendored
Normal file
138
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/media-has-caption-test.js
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview <audio> and <video> elements must have a <track> for captions.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/media-has-caption';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Media elements such as <audio> and <video> must have a <track> for captions.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const customSchema = [
|
||||
{
|
||||
audio: ['Audio'],
|
||||
video: ['Video'],
|
||||
track: ['Track'],
|
||||
},
|
||||
];
|
||||
|
||||
ruleTester.run('media-has-caption', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<MyDiv />;' },
|
||||
{ code: '<audio><track kind="captions" /></audio>' },
|
||||
{ code: '<audio><track kind="Captions" /></audio>' },
|
||||
{
|
||||
code: '<audio><track kind="Captions" /><track kind="subtitles" /></audio>',
|
||||
},
|
||||
{ code: '<video><track kind="captions" /></video>' },
|
||||
{ code: '<video><track kind="Captions" /></video>' },
|
||||
{
|
||||
code: '<video><track kind="Captions" /><track kind="subtitles" /></video>',
|
||||
},
|
||||
{
|
||||
code: '<audio muted={true}></audio>',
|
||||
},
|
||||
{
|
||||
code: '<video muted={true}></video>',
|
||||
},
|
||||
{
|
||||
code: '<video muted></video>',
|
||||
},
|
||||
{
|
||||
code: '<Audio><track kind="captions" /></Audio>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<audio><Track kind="captions" /></audio>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Video><track kind="captions" /></Video>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<video><Track kind="captions" /></video>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Audio><Track kind="captions" /></Audio>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Video><Track kind="captions" /></Video>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Video muted></Video>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Video muted={true}></Video>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Audio muted></Audio>',
|
||||
options: customSchema,
|
||||
},
|
||||
{
|
||||
code: '<Audio muted={true}></Audio>',
|
||||
options: customSchema,
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<audio><track /></audio>', errors: [expectedError] },
|
||||
{
|
||||
code: '<audio><track kind="subtitles" /></audio>',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<audio />', errors: [expectedError] },
|
||||
{ code: '<video><track /></video>', errors: [expectedError] },
|
||||
{
|
||||
code: '<video><track kind="subtitles" /></video>',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<Audio muted={false}></Audio>',
|
||||
options: customSchema,
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<Video muted={false}></Video>',
|
||||
options: customSchema,
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<video />', errors: [expectedError] },
|
||||
{ code: '<audio>Foo</audio>', errors: [expectedError] },
|
||||
{ code: '<video>Foo</video>', errors: [expectedError] },
|
||||
{ code: '<Audio />', options: customSchema, errors: [expectedError] },
|
||||
{ code: '<Video />', options: customSchema, errors: [expectedError] },
|
||||
{ code: '<audio><Track /></audio>', options: customSchema, errors: [expectedError] },
|
||||
{ code: '<video><Track /></video>', options: customSchema, errors: [expectedError] },
|
||||
{
|
||||
code: '<Audio><Track kind="subtitles" /></Audio>',
|
||||
options: customSchema,
|
||||
errors: [expectedError],
|
||||
},
|
||||
{
|
||||
code: '<Video><Track kind="subtitles" /></Video>',
|
||||
options: customSchema,
|
||||
errors: [expectedError],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
78
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/mouse-events-have-key-events-test.js
generated
vendored
Normal file
78
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/mouse-events-have-key-events-test.js
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce onmouseover/onmouseout are accompanied
|
||||
* by onfocus/onblur.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/mouse-events-have-key-events';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const mouseOverError = {
|
||||
message: 'onMouseOver must be accompanied by onFocus for accessibility.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
const mouseOutError = {
|
||||
message: 'onMouseOut must be accompanied by onBlur for accessibility.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('mouse-events-have-key-events', rule, {
|
||||
valid: [
|
||||
{ code: '<div onMouseOver={() => void 0} onFocus={() => void 0} />;' },
|
||||
{
|
||||
code: '<div onMouseOver={() => void 0} onFocus={() => void 0} {...props} />;',
|
||||
},
|
||||
{ code: '<div onMouseOver={handleMouseOver} onFocus={handleFocus} />;' },
|
||||
{
|
||||
code: '<div onMouseOver={handleMouseOver} onFocus={handleFocus} {...props} />;',
|
||||
},
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div onBlur={() => {}} />' },
|
||||
{ code: '<div onFocus={() => {}} />' },
|
||||
{ code: '<div onMouseOut={() => void 0} onBlur={() => void 0} />' },
|
||||
{ code: '<div onMouseOut={() => void 0} onBlur={() => void 0} {...props} />' },
|
||||
{ code: '<div onMouseOut={handleMouseOut} onBlur={handleOnBlur} />' },
|
||||
{ code: '<div onMouseOut={handleMouseOut} onBlur={handleOnBlur} {...props} />' },
|
||||
{ code: '<MyElement />' },
|
||||
{ code: '<MyElement onMouseOver={() => {}} />' },
|
||||
{ code: '<MyElement onMouseOut={() => {}} />' },
|
||||
{ code: '<MyElement onBlur={() => {}} />' },
|
||||
{ code: '<MyElement onFocus={() => {}} />' },
|
||||
{ code: '<MyElement onMouseOver={() => {}} {...props} />' },
|
||||
{ code: '<MyElement onMouseOut={() => {}} {...props} />' },
|
||||
{ code: '<MyElement onBlur={() => {}} {...props} />' },
|
||||
{ code: '<MyElement onFocus={() => {}} {...props} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div onMouseOver={() => void 0} />;', errors: [mouseOverError] },
|
||||
{ code: '<div onMouseOut={() => void 0} />', errors: [mouseOutError] },
|
||||
{
|
||||
code: '<div onMouseOver={() => void 0} onFocus={undefined} />;',
|
||||
errors: [mouseOverError],
|
||||
},
|
||||
{
|
||||
code: '<div onMouseOut={() => void 0} onBlur={undefined} />',
|
||||
errors: [mouseOutError],
|
||||
},
|
||||
{
|
||||
code: '<div onMouseOver={() => void 0} {...props} />',
|
||||
errors: [mouseOverError],
|
||||
},
|
||||
{
|
||||
code: '<div onMouseOut={() => void 0} {...props} />',
|
||||
errors: [mouseOutError],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
48
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-access-key-test.js
generated
vendored
Normal file
48
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-access-key-test.js
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce no accesskey attribute on element.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-access-key';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'No access key attribute allowed. Inconsistencies between keyboard shortcuts and keyboard comments used by screenreader and keyboard only users create a11y complications.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('no-access-key', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div {...props} />' },
|
||||
{ code: '<div accessKey={undefined} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div accesskey="h" />', errors: [expectedError] },
|
||||
{ code: '<div accessKey="h" />', errors: [expectedError] },
|
||||
{ code: '<div accessKey="h" {...props} />', errors: [expectedError] },
|
||||
{ code: '<div acCesSKeY="y" />', errors: [expectedError] },
|
||||
{ code: '<div accessKey={"y"} />', errors: [expectedError] },
|
||||
{ code: '<div accessKey={`${y}`} />', errors: [expectedError] },
|
||||
{
|
||||
code: '<div accessKey={`${undefined}y${undefined}`} />',
|
||||
errors: [expectedError],
|
||||
},
|
||||
{ code: '<div accessKey={`This is ${bad}`} />', errors: [expectedError] },
|
||||
{ code: '<div accessKey={accessKey} />', errors: [expectedError] },
|
||||
{ code: '<div accessKey={`${undefined}`} />', errors: [expectedError] },
|
||||
{ code: '<div accessKey={`${undefined}${undefined}`} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
51
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-autofocus-test.js
generated
vendored
Normal file
51
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-autofocus-test.js
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce autoFocus prop is not used.
|
||||
* @author Ethan Cohen <@evcohen>
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-autofocus';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'The autoFocus prop should not be used, as it can reduce usability and accessibility for users.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
const ignoreNonDOMSchema = [
|
||||
{
|
||||
ignoreNonDOM: true,
|
||||
},
|
||||
];
|
||||
|
||||
ruleTester.run('no-autofocus', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div autofocus />;' },
|
||||
{ code: '<input autofocus="true" />;' },
|
||||
{ code: '<Foo bar />' },
|
||||
{ code: '<Foo autoFocus />', options: ignoreNonDOMSchema },
|
||||
{ code: '<div><div autofocus /></div>', options: ignoreNonDOMSchema },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div autoFocus />', errors: [expectedError] },
|
||||
{ code: '<div autoFocus={true} />', errors: [expectedError] },
|
||||
{ code: '<div autoFocus={false} />', errors: [expectedError] },
|
||||
{ code: '<div autoFocus={undefined} />', errors: [expectedError] },
|
||||
{ code: '<div autoFocus="true" />', errors: [expectedError] },
|
||||
{ code: '<div autoFocus="false" />', errors: [expectedError] },
|
||||
{ code: '<input autoFocus />', errors: [expectedError] },
|
||||
{ code: '<Foo autoFocus />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
42
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-distracting-elements-test.js
generated
vendored
Normal file
42
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-distracting-elements-test.js
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce distracting elements are not used.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-distracting-elements';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = (element) => ({
|
||||
message: `Do not use <${element}> elements as they can create visual accessibility issues and are deprecated.`,
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
ruleTester.run('no-marquee', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<Marquee />' },
|
||||
{ code: '<div marquee />' },
|
||||
{ code: '<Blink />' },
|
||||
{ code: '<div blink />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<marquee />', errors: [expectedError('marquee')] },
|
||||
{ code: '<marquee {...props} />', errors: [expectedError('marquee')] },
|
||||
{ code: '<marquee lang={undefined} />', errors: [expectedError('marquee')] },
|
||||
{ code: '<blink />', errors: [expectedError('blink')] },
|
||||
{ code: '<blink {...props} />', errors: [expectedError('blink')] },
|
||||
{ code: '<blink foo={undefined} />', errors: [expectedError('blink')] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
390
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js
generated
vendored
Normal file
390
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js
generated
vendored
Normal file
|
@ -0,0 +1,390 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Disallow inherently interactive elements to be assigned
|
||||
* non-interactive roles.
|
||||
* @author Jesse Beach
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-interactive-element-to-noninteractive-role';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = 'Interactive elements should not be assigned non-interactive roles.';
|
||||
|
||||
const expectedError = {
|
||||
message: errorMessage,
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
const ruleName = 'jsx-a11y/no-interactive-element-to-noninteractive-role';
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<Button onClick={doFoo} />' },
|
||||
/* Interactive elements */
|
||||
{ code: '<a href="http://x.y.z" role="button" />' },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="button" />' },
|
||||
{ code: '<button className="foo" role="button" />' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="button" />' },
|
||||
{ code: '<input type="button" role="button" />' },
|
||||
{ code: '<input type="checkbox" role="button" />' },
|
||||
{ code: '<input type="color" role="button" />' },
|
||||
{ code: '<input type="date" role="button" />' },
|
||||
{ code: '<input type="datetime" role="button" />' },
|
||||
{ code: '<input type="datetime-local" role="button" />' },
|
||||
{ code: '<input type="email" role="button" />' },
|
||||
{ code: '<input type="file" role="button" />' },
|
||||
{ code: '<input type="image" role="button" />' },
|
||||
{ code: '<input type="month" role="button" />' },
|
||||
{ code: '<input type="number" role="button" />' },
|
||||
{ code: '<input type="password" role="button" />' },
|
||||
{ code: '<input type="radio" role="button" />' },
|
||||
{ code: '<input type="range" role="button" />' },
|
||||
{ code: '<input type="reset" role="button" />' },
|
||||
{ code: '<input type="search" role="button" />' },
|
||||
{ code: '<input type="submit" role="button" />' },
|
||||
{ code: '<input type="tel" role="button" />' },
|
||||
{ code: '<input type="text" role="button" />' },
|
||||
{ code: '<input type="time" role="button" />' },
|
||||
{ code: '<input type="url" role="button" />' },
|
||||
{ code: '<input type="week" role="button" />' },
|
||||
{ code: '<input type="hidden" role="button" />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="button" />;' },
|
||||
{ code: '<option className="foo" role="button" />' },
|
||||
{ code: '<select className="foo" role="button" />' },
|
||||
{ code: '<textarea className="foo" role="button" />' },
|
||||
{ code: '<tr role="button" />;' },
|
||||
/* HTML elements with neither an interactive or non-interactive valence (static) */
|
||||
{ code: '<a role="button" />' },
|
||||
{ code: '<a role="img" />;' },
|
||||
{ code: '<a tabIndex="0" role="button" />' },
|
||||
{ code: '<a tabIndex="0" role="img" />' },
|
||||
{ code: '<acronym role="button" />;' },
|
||||
{ code: '<address role="button" />;' },
|
||||
{ code: '<applet role="button" />;' },
|
||||
{ code: '<aside role="button" />;' },
|
||||
{ code: '<audio role="button" />;' },
|
||||
{ code: '<b role="button" />;' },
|
||||
{ code: '<base role="button" />;' },
|
||||
{ code: '<bdi role="button" />;' },
|
||||
{ code: '<bdo role="button" />;' },
|
||||
{ code: '<big role="button" />;' },
|
||||
{ code: '<blink role="button" />;' },
|
||||
{ code: '<blockquote role="button" />;' },
|
||||
{ code: '<body role="button" />;' },
|
||||
{ code: '<br role="button" />;' },
|
||||
{ code: '<canvas role="button" />;' },
|
||||
{ code: '<caption role="button" />;' },
|
||||
{ code: '<center role="button" />;' },
|
||||
{ code: '<cite role="button" />;' },
|
||||
{ code: '<code role="button" />;' },
|
||||
{ code: '<col role="button" />;' },
|
||||
{ code: '<colgroup role="button" />;' },
|
||||
{ code: '<content role="button" />;' },
|
||||
{ code: '<data role="button" />;' },
|
||||
{ code: '<datalist role="button" />;' },
|
||||
{ code: '<del role="button" />;' },
|
||||
{ code: '<details role="button" />;' },
|
||||
{ code: '<dir role="button" />;' },
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div className="foo" role="button" />;' },
|
||||
{ code: '<div className="foo" {...props} role="button" />;' },
|
||||
{ code: '<div aria-hidden role="button" />;' },
|
||||
{ code: '<div aria-hidden={true} role="button" />;' },
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div role={undefined} role="button" />;' },
|
||||
{ code: '<div {...props} role="button" />;' },
|
||||
{ code: '<div onKeyUp={() => void 0} aria-hidden={false} role="button" />;' },
|
||||
{ code: '<dl role="button" />;' },
|
||||
{ code: '<em role="button" />;' },
|
||||
{ code: '<embed role="button" />;' },
|
||||
{ code: '<figcaption role="button" />;' },
|
||||
{ code: '<font role="button" />;' },
|
||||
{ code: '<footer role="button" />;' },
|
||||
{ code: '<frameset role="button" />;' },
|
||||
{ code: '<head role="button" />;' },
|
||||
{ code: '<header role="button" />;' },
|
||||
{ code: '<hgroup role="button" />;' },
|
||||
{ code: '<html role="button" />;' },
|
||||
{ code: '<i role="button" />;' },
|
||||
{ code: '<iframe role="button" />;' },
|
||||
{ code: '<ins role="button" />;' },
|
||||
{ code: '<kbd role="button" />;' },
|
||||
{ code: '<keygen role="button" />;' },
|
||||
{ code: '<label role="button" />;' },
|
||||
{ code: '<legend role="button" />;' },
|
||||
{ code: '<link role="button" />;' },
|
||||
{ code: '<map role="button" />;' },
|
||||
{ code: '<mark role="button" />;' },
|
||||
{ code: '<marquee role="button" />;' },
|
||||
{ code: '<menu role="button" />;' },
|
||||
{ code: '<meta role="button" />;' },
|
||||
{ code: '<meter role="button" />;' },
|
||||
{ code: '<noembed role="button" />;' },
|
||||
{ code: '<noscript role="button" />;' },
|
||||
{ code: '<object role="button" />;' },
|
||||
{ code: '<optgroup role="button" />;' },
|
||||
{ code: '<output role="button" />;' },
|
||||
{ code: '<p role="button" />;' },
|
||||
{ code: '<param role="button" />;' },
|
||||
{ code: '<picture role="button" />;' },
|
||||
{ code: '<pre role="button" />;' },
|
||||
{ code: '<progress role="button" />;' },
|
||||
{ code: '<q role="button" />;' },
|
||||
{ code: '<rp role="button" />;' },
|
||||
{ code: '<rt role="button" />;' },
|
||||
{ code: '<rtc role="button" />;' },
|
||||
{ code: '<ruby role="button" />;' },
|
||||
{ code: '<s role="button" />;' },
|
||||
{ code: '<samp role="button" />;' },
|
||||
{ code: '<script role="button" />;' },
|
||||
{ code: '<section role="button" />;' },
|
||||
{ code: '<small role="button" />;' },
|
||||
{ code: '<source role="button" />;' },
|
||||
{ code: '<spacer role="button" />;' },
|
||||
{ code: '<span role="button" />;' },
|
||||
{ code: '<strike role="button" />;' },
|
||||
{ code: '<strong role="button" />;' },
|
||||
{ code: '<style role="button" />;' },
|
||||
{ code: '<sub role="button" />;' },
|
||||
{ code: '<summary role="button" />;' },
|
||||
{ code: '<sup role="button" />;' },
|
||||
{ code: '<th role="button" />;' },
|
||||
{ code: '<time role="button" />;' },
|
||||
{ code: '<title role="button" />;' },
|
||||
{ code: '<track role="button" />;' },
|
||||
{ code: '<tt role="button" />;' },
|
||||
{ code: '<u role="button" />;' },
|
||||
{ code: '<var role="button" />;' },
|
||||
{ code: '<video role="button" />;' },
|
||||
{ code: '<wbr role="button" />;' },
|
||||
{ code: '<xmp role="button" />;' },
|
||||
/* HTML elements attributed with an interactive role */
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div role="checkbox" />;' },
|
||||
{ code: '<div role="columnheader" />;' },
|
||||
{ code: '<div role="combobox" />;' },
|
||||
{ code: '<div role="grid" />;' },
|
||||
{ code: '<div role="gridcell" />;' },
|
||||
{ code: '<div role="link" />;' },
|
||||
{ code: '<div role="listbox" />;' },
|
||||
{ code: '<div role="menu" />;' },
|
||||
{ code: '<div role="menubar" />;' },
|
||||
{ code: '<div role="menuitem" />;' },
|
||||
{ code: '<div role="menuitemcheckbox" />;' },
|
||||
{ code: '<div role="menuitemradio" />;' },
|
||||
{ code: '<div role="option" />;' },
|
||||
{ code: '<div role="progressbar" />;' },
|
||||
{ code: '<div role="radio" />;' },
|
||||
{ code: '<div role="radiogroup" />;' },
|
||||
{ code: '<div role="row" />;' },
|
||||
{ code: '<div role="rowheader" />;' },
|
||||
{ code: '<div role="searchbox" />;' },
|
||||
{ code: '<div role="slider" />;' },
|
||||
{ code: '<div role="spinbutton" />;' },
|
||||
{ code: '<div role="switch" />;' },
|
||||
{ code: '<div role="tab" />;' },
|
||||
{ code: '<div role="textbox" />;' },
|
||||
{ code: '<div role="treeitem" />;' },
|
||||
/* Presentation is a special case role that indicates intentional static semantics */
|
||||
{ code: '<div role="presentation" />;' },
|
||||
/* HTML elements attributed with an abstract role */
|
||||
{ code: '<div role="command" />;' },
|
||||
{ code: '<div role="composite" />;' },
|
||||
{ code: '<div role="input" />;' },
|
||||
{ code: '<div role="landmark" />;' },
|
||||
{ code: '<div role="range" />;' },
|
||||
{ code: '<div role="roletype" />;' },
|
||||
{ code: '<div role="section" />;' },
|
||||
{ code: '<div role="sectionhead" />;' },
|
||||
{ code: '<div role="select" />;' },
|
||||
{ code: '<div role="structure" />;' },
|
||||
{ code: '<div role="tablist" />;' },
|
||||
{ code: '<div role="toolbar" />;' },
|
||||
{ code: '<div role="tree" />;' },
|
||||
{ code: '<div role="treegrid" />;' },
|
||||
{ code: '<div role="widget" />;' },
|
||||
{ code: '<div role="window" />;' },
|
||||
/* HTML elements with an inherent, non-interactive role, assigned an
|
||||
* interactive role. */
|
||||
{ code: '<main role="button" />;' },
|
||||
{ code: '<area role="button" />;' },
|
||||
{ code: '<article role="button" />;' },
|
||||
{ code: '<article role="button" />;' },
|
||||
{ code: '<dd role="button" />;' },
|
||||
{ code: '<dfn role="button" />;' },
|
||||
{ code: '<dt role="button" />;' },
|
||||
{ code: '<fieldset role="button" />;' },
|
||||
{ code: '<figure role="button" />;' },
|
||||
{ code: '<form role="button" />;' },
|
||||
{ code: '<frame role="button" />;' },
|
||||
{ code: '<h1 role="button" />;' },
|
||||
{ code: '<h2 role="button" />;' },
|
||||
{ code: '<h3 role="button" />;' },
|
||||
{ code: '<h4 role="button" />;' },
|
||||
{ code: '<h5 role="button" />;' },
|
||||
{ code: '<h6 role="button" />;' },
|
||||
{ code: '<hr role="button" />;' },
|
||||
{ code: '<img role="button" />;' },
|
||||
{ code: '<li role="button" />;' },
|
||||
{ code: '<li role="presentation" />;' },
|
||||
{ code: '<nav role="button" />;' },
|
||||
{ code: '<ol role="button" />;' },
|
||||
{ code: '<table role="button" />;' },
|
||||
{ code: '<tbody role="button" />;' },
|
||||
{ code: '<td role="button" />;' },
|
||||
{ code: '<tfoot role="button" />;' },
|
||||
{ code: '<thead role="button" />;' },
|
||||
{ code: '<ul role="button" />;' },
|
||||
/* HTML elements attributed with a non-interactive role */
|
||||
{ code: '<div role="alert" />;' },
|
||||
{ code: '<div role="alertdialog" />;' },
|
||||
{ code: '<div role="application" />;' },
|
||||
{ code: '<div role="article" />;' },
|
||||
{ code: '<div role="banner" />;' },
|
||||
{ code: '<div role="cell" />;' },
|
||||
{ code: '<div role="complementary" />;' },
|
||||
{ code: '<div role="contentinfo" />;' },
|
||||
{ code: '<div role="definition" />;' },
|
||||
{ code: '<div role="dialog" />;' },
|
||||
{ code: '<div role="directory" />;' },
|
||||
{ code: '<div role="document" />;' },
|
||||
{ code: '<div role="feed" />;' },
|
||||
{ code: '<div role="figure" />;' },
|
||||
{ code: '<div role="form" />;' },
|
||||
{ code: '<div role="group" />;' },
|
||||
{ code: '<div role="heading" />;' },
|
||||
{ code: '<div role="img" />;' },
|
||||
{ code: '<div role="list" />;' },
|
||||
{ code: '<div role="listitem" />;' },
|
||||
{ code: '<div role="log" />;' },
|
||||
{ code: '<div role="main" />;' },
|
||||
{ code: '<div role="marquee" />;' },
|
||||
{ code: '<div role="math" />;' },
|
||||
{ code: '<div role="navigation" />;' },
|
||||
{ code: '<div role="note" />;' },
|
||||
{ code: '<div role="region" />;' },
|
||||
{ code: '<div role="rowgroup" />;' },
|
||||
{ code: '<div role="search" />;' },
|
||||
{ code: '<div role="separator" />;' },
|
||||
{ code: '<div role="scrollbar" />;' },
|
||||
{ code: '<div role="status" />;' },
|
||||
{ code: '<div role="table" />;' },
|
||||
{ code: '<div role="tabpanel" />;' },
|
||||
{ code: '<div role="term" />;' },
|
||||
{ code: '<div role="timer" />;' },
|
||||
{ code: '<div role="tooltip" />;' },
|
||||
/* Namespaced roles are not checked */
|
||||
{ code: '<div mynamespace:role="term" />' },
|
||||
{ code: '<input mynamespace:role="img" />' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
/* Interactive elements */
|
||||
{ code: '<a href="http://x.y.z" role="img" />', errors: [expectedError] },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="img" />', errors: [expectedError] },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="img" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="checkbox" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="color" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="date" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="datetime" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="datetime-local" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="email" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="file" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="hidden" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="image" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="month" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="number" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="password" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="radio" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="range" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="reset" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="search" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="submit" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="tel" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="text" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="time" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="url" role="img" />', errors: [expectedError] },
|
||||
{ code: '<input type="week" role="img" />', errors: [expectedError] },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="img" />;', errors: [expectedError] },
|
||||
{ code: '<option className="foo" role="img" />', errors: [expectedError] },
|
||||
{ code: '<select className="foo" role="img" />', errors: [expectedError] },
|
||||
{ code: '<textarea className="foo" role="img" />', errors: [expectedError] },
|
||||
{ code: '<tr role="img" />;', errors: [expectedError] },
|
||||
/* Interactive elements */
|
||||
{ code: '<a href="http://x.y.z" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="listitem" />', errors: [expectedError] },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="listitem" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="checkbox" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="color" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="date" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="datetime" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="datetime-local" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="email" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="file" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="image" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="month" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="number" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="password" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="radio" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="range" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="reset" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="search" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="submit" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="tel" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="text" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="time" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="url" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<input type="week" role="listitem" />', errors: [expectedError] },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="listitem" />;', errors: [expectedError] },
|
||||
{ code: '<option className="foo" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<select className="foo" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<summary role="listitem" />;', errors: [expectedError] },
|
||||
{ code: '<textarea className="foo" role="listitem" />', errors: [expectedError] },
|
||||
{ code: '<tr role="listitem" />;', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
{ code: '<tr role="presentation" />;' },
|
||||
{ code: '<Component role="presentation" />;' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
{ code: '<tr role="presentation" />;', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
467
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-element-interactions-test.js
generated
vendored
Normal file
467
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-element-interactions-test.js
generated
vendored
Normal file
|
@ -0,0 +1,467 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce non-interactive elements have no interactive handlers.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-noninteractive-element-interactions';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = 'Non-interactive elements should not be assigned mouse or keyboard event listeners.';
|
||||
|
||||
const expectedError = {
|
||||
message: errorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const ruleName = 'no-noninteractive-element-interactions';
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<Button onClick={doFoo} />' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input onClick={() => void 0} />' },
|
||||
{ code: '<input type="button" onClick={() => void 0} />' },
|
||||
{ code: '<input type="checkbox" onClick={() => void 0} />' },
|
||||
{ code: '<input type="color" onClick={() => void 0} />' },
|
||||
{ code: '<input type="date" onClick={() => void 0} />' },
|
||||
{ code: '<input type="datetime" onClick={() => void 0} />' },
|
||||
{ code: '<input type="datetime-local" onClick={() => void 0} />' },
|
||||
{ code: '<input type="email" onClick={() => void 0} />' },
|
||||
{ code: '<input type="file" onClick={() => void 0} />' },
|
||||
{ code: '<input type="image" onClick={() => void 0} />' },
|
||||
{ code: '<input type="month" onClick={() => void 0} />' },
|
||||
{ code: '<input type="number" onClick={() => void 0} />' },
|
||||
{ code: '<input type="password" onClick={() => void 0} />' },
|
||||
{ code: '<input type="radio" onClick={() => void 0} />' },
|
||||
{ code: '<input type="range" onClick={() => void 0} />' },
|
||||
{ code: '<input type="reset" onClick={() => void 0} />' },
|
||||
{ code: '<input type="search" onClick={() => void 0} />' },
|
||||
{ code: '<input type="submit" onClick={() => void 0} />' },
|
||||
{ code: '<input type="tel" onClick={() => void 0} />' },
|
||||
{ code: '<input type="text" onClick={() => void 0} />' },
|
||||
{ code: '<input type="time" onClick={() => void 0} />' },
|
||||
{ code: '<input type="url" onClick={() => void 0} />' },
|
||||
{ code: '<input type="week" onClick={() => void 0} />' },
|
||||
{ code: '<input type="hidden" onClick={() => void 0} />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<a onClick={() => void 0} />' },
|
||||
{ code: '<a onClick={() => {}} />;' },
|
||||
{ code: '<a tabIndex="0" onClick={() => void 0} />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
|
||||
{ code: '<area onClick={() => {}} />;' },
|
||||
{ code: '<button onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<menuitem onClick={() => {}} />;' },
|
||||
{ code: '<option onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<select onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<textarea onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<tr onClick={() => {}} />;' },
|
||||
/* HTML elements with neither an interactive or non-interactive valence (static) */
|
||||
{ code: '<acronym onClick={() => {}} />;' },
|
||||
{ code: '<address onClick={() => {}} />;' },
|
||||
{ code: '<applet onClick={() => {}} />;' },
|
||||
{ code: '<audio onClick={() => {}} />;' },
|
||||
{ code: '<b onClick={() => {}} />;' },
|
||||
{ code: '<base onClick={() => {}} />;' },
|
||||
{ code: '<bdi onClick={() => {}} />;' },
|
||||
{ code: '<bdo onClick={() => {}} />;' },
|
||||
{ code: '<big onClick={() => {}} />;' },
|
||||
{ code: '<blink onClick={() => {}} />;' },
|
||||
{ code: '<body onLoad={() => {}} />;' },
|
||||
{ code: '<canvas onClick={() => {}} />;' },
|
||||
{ code: '<center onClick={() => {}} />;' },
|
||||
{ code: '<cite onClick={() => {}} />;' },
|
||||
{ code: '<code onClick={() => {}} />;' },
|
||||
{ code: '<col onClick={() => {}} />;' },
|
||||
{ code: '<colgroup onClick={() => {}} />;' },
|
||||
{ code: '<content onClick={() => {}} />;' },
|
||||
{ code: '<data onClick={() => {}} />;' },
|
||||
{ code: '<datalist onClick={() => {}} />;' },
|
||||
{ code: '<del onClick={() => {}} />;' },
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div className="foo" />;' },
|
||||
{ code: '<div className="foo" {...props} />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden={true} />;' },
|
||||
{ code: '<div onClick={() => void 0} />;' },
|
||||
{ code: '<div onClick={() => void 0} role={undefined} />;' },
|
||||
{ code: '<div onClick={() => void 0} {...props} />;' },
|
||||
{ code: '<div onClick={null} />;' },
|
||||
{ code: '<div onKeyUp={() => void 0} aria-hidden={false} />;' },
|
||||
{ code: '<em onClick={() => {}} />;' },
|
||||
{ code: '<embed onClick={() => {}} />;' },
|
||||
{ code: '<font onClick={() => {}} />;' },
|
||||
{ code: '<font onSubmit={() => {}} />;' },
|
||||
{ code: '<frameset onClick={() => {}} />;' },
|
||||
{ code: '<head onClick={() => {}} />;' },
|
||||
{ code: '<header onClick={() => {}} />;' },
|
||||
{ code: '<hgroup onClick={() => {}} />;' },
|
||||
{ code: '<html onClick={() => {}} />;' },
|
||||
{ code: '<i onClick={() => {}} />;' },
|
||||
{ code: '<iframe onLoad={() => {}} />;' },
|
||||
{
|
||||
code: `
|
||||
<iframe
|
||||
name="embeddedExternalPayment"
|
||||
ref="embeddedExternalPayment"
|
||||
style={iframeStyle}
|
||||
onLoad={this.handleLoadIframe}
|
||||
/>
|
||||
`,
|
||||
},
|
||||
{ code: '<img {...props} onError={() => {}} />;' },
|
||||
{ code: '<img onLoad={() => {}} />;' },
|
||||
{ code: '<ins onClick={() => {}} />;' },
|
||||
{ code: '<kbd onClick={() => {}} />;' },
|
||||
{ code: '<keygen onClick={() => {}} />;' },
|
||||
{ code: '<link onClick={() => {}} />;' },
|
||||
{ code: '<main onClick={null} />;' },
|
||||
{ code: '<map onClick={() => {}} />;' },
|
||||
{ code: '<meta onClick={() => {}} />;' },
|
||||
{ code: '<noembed onClick={() => {}} />;' },
|
||||
{ code: '<noscript onClick={() => {}} />;' },
|
||||
{ code: '<object onClick={() => {}} />;' },
|
||||
{ code: '<param onClick={() => {}} />;' },
|
||||
{ code: '<picture onClick={() => {}} />;' },
|
||||
{ code: '<q onClick={() => {}} />;' },
|
||||
{ code: '<rp onClick={() => {}} />;' },
|
||||
{ code: '<rt onClick={() => {}} />;' },
|
||||
{ code: '<rtc onClick={() => {}} />;' },
|
||||
{ code: '<s onClick={() => {}} />;' },
|
||||
{ code: '<samp onClick={() => {}} />;' },
|
||||
{ code: '<script onClick={() => {}} />;' },
|
||||
{ code: '<section onClick={() => {}} />;' },
|
||||
{ code: '<small onClick={() => {}} />;' },
|
||||
{ code: '<source onClick={() => {}} />;' },
|
||||
{ code: '<spacer onClick={() => {}} />;' },
|
||||
{ code: '<span onClick={() => {}} />;' },
|
||||
{ code: '<strike onClick={() => {}} />;' },
|
||||
{ code: '<strong onClick={() => {}} />;' },
|
||||
{ code: '<style onClick={() => {}} />;' },
|
||||
{ code: '<sub onClick={() => {}} />;' },
|
||||
{ code: '<summary onClick={() => {}} />;' },
|
||||
{ code: '<sup onClick={() => {}} />;' },
|
||||
{ code: '<th onClick={() => {}} />;' },
|
||||
{ code: '<title onClick={() => {}} />;' },
|
||||
{ code: '<track onClick={() => {}} />;' },
|
||||
{ code: '<tt onClick={() => {}} />;' },
|
||||
{ code: '<u onClick={() => {}} />;' },
|
||||
{ code: '<var onClick={() => {}} />;' },
|
||||
{ code: '<video onClick={() => {}} />;' },
|
||||
{ code: '<wbr onClick={() => {}} />;' },
|
||||
{ code: '<xmp onClick={() => {}} />;' },
|
||||
/* HTML elements attributed with an interactive role */
|
||||
{ code: '<div role="button" onClick={() => {}} />;' },
|
||||
{ code: '<div role="checkbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="columnheader" onClick={() => {}} />;' },
|
||||
{ code: '<div role="combobox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="grid" onClick={() => {}} />;' },
|
||||
{ code: '<div role="gridcell" onClick={() => {}} />;' },
|
||||
{ code: '<div role="link" onClick={() => {}} />;' },
|
||||
{ code: '<div role="listbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menu" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menubar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitem" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitemcheckbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitemradio" onClick={() => {}} />;' },
|
||||
{ code: '<div role="option" onClick={() => {}} />;' },
|
||||
{ code: '<div role="progressbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="radio" onClick={() => {}} />;' },
|
||||
{ code: '<div role="radiogroup" onClick={() => {}} />;' },
|
||||
{ code: '<div role="row" onClick={() => {}} />;' },
|
||||
{ code: '<div role="rowheader" onClick={() => {}} />;' },
|
||||
{ code: '<div role="scrollbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="searchbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="slider" onClick={() => {}} />;' },
|
||||
{ code: '<div role="spinbutton" onClick={() => {}} />;' },
|
||||
{ code: '<div role="switch" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tab" onClick={() => {}} />;' },
|
||||
{ code: '<div role="textbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="treeitem" onClick={() => {}} />;' },
|
||||
/* Presentation is a special case role that indicates intentional static semantics */
|
||||
{ code: '<div role="presentation" onClick={() => {}} />;' },
|
||||
/* HTML elements attributed with an abstract role */
|
||||
{ code: '<div role="command" onClick={() => {}} />;' },
|
||||
{ code: '<div role="composite" onClick={() => {}} />;' },
|
||||
{ code: '<div role="input" onClick={() => {}} />;' },
|
||||
{ code: '<div role="landmark" onClick={() => {}} />;' },
|
||||
{ code: '<div role="range" onClick={() => {}} />;' },
|
||||
{ code: '<div role="roletype" onClick={() => {}} />;' },
|
||||
{ code: '<div role="sectionhead" onClick={() => {}} />;' },
|
||||
{ code: '<div role="select" onClick={() => {}} />;' },
|
||||
{ code: '<div role="structure" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tablist" onClick={() => {}} />;' },
|
||||
{ code: '<div role="toolbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tree" onClick={() => {}} />;' },
|
||||
{ code: '<div role="treegrid" onClick={() => {}} />;' },
|
||||
{ code: '<div role="widget" onClick={() => {}} />;' },
|
||||
{ code: '<div role="window" onClick={() => {}} />;' },
|
||||
// All the possible handlers
|
||||
{ code: '<div role="article" onCopy={() => {}} />;' },
|
||||
{ code: '<div role="article" onCut={() => {}} />;' },
|
||||
{ code: '<div role="article" onPaste={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionUpdate={() => {}} />;' },
|
||||
{ code: '<div role="article" onChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onInput={() => {}} />;' },
|
||||
{ code: '<div role="article" onSubmit={() => {}} />;' },
|
||||
{ code: '<div role="article" onSelect={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchCancel={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchMove={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onScroll={() => {}} />;' },
|
||||
{ code: '<div role="article" onWheel={() => {}} />;' },
|
||||
{ code: '<div role="article" onAbort={() => {}} />;' },
|
||||
{ code: '<div role="article" onCanPlay={() => {}} />;' },
|
||||
{ code: '<div role="article" onCanPlayThrough={() => {}} />;' },
|
||||
{ code: '<div role="article" onDurationChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onEmptied={() => {}} />;' },
|
||||
{ code: '<div role="article" onEncrypted={() => {}} />;' },
|
||||
{ code: '<div role="article" onEnded={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadedData={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadedMetadata={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onPause={() => {}} />;' },
|
||||
{ code: '<div role="article" onPlay={() => {}} />;' },
|
||||
{ code: '<div role="article" onPlaying={() => {}} />;' },
|
||||
{ code: '<div role="article" onProgress={() => {}} />;' },
|
||||
{ code: '<div role="article" onRateChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onSeeked={() => {}} />;' },
|
||||
{ code: '<div role="article" onSeeking={() => {}} />;' },
|
||||
{ code: '<div role="article" onStalled={() => {}} />;' },
|
||||
{ code: '<div role="article" onSuspend={() => {}} />;' },
|
||||
{ code: '<div role="article" onTimeUpdate={() => {}} />;' },
|
||||
{ code: '<div role="article" onVolumeChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onWaiting={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationIteration={() => {}} />;' },
|
||||
{ code: '<div role="article" onTransitionEnd={() => {}} />;' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
/* HTML elements with an inherent, non-interactive role */
|
||||
{ code: '<main onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<article onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<aside onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<blockquote onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<body onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<br onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<caption onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<dd onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<details onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<dfn onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<dl onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<dir onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<dt onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<fieldset onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<figcaption onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<figure onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<footer onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<form onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<frame onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h1 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h2 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h3 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h4 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h5 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<h6 onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<hr onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<iframe onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<img onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<label onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<legend onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<li onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<mark onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<marquee onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<menu onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<meter onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<nav onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<ol onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<optgroup onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<output onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<p onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<pre onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<progress onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<ruby onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<section onClick={() => {}} aria-label="Aardvark" />;', errors: [expectedError] },
|
||||
{ code: '<section onClick={() => {}} aria-labelledby="js_1" />;', errors: [expectedError] },
|
||||
{ code: '<table onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<tbody onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<td onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<tfoot onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<thead onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<time onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<ol onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<ul onClick={() => {}} />;', errors: [expectedError] },
|
||||
/* HTML elements attributed with a non-interactive role */
|
||||
{ code: '<div role="alert" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="alertdialog" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="application" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="banner" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="cell" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="complementary" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="contentinfo" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="definition" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="dialog" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="directory" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="document" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="feed" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="figure" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="form" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="group" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="heading" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="img" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="list" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="listitem" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="log" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="main" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="marquee" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="math" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="navigation" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="note" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="region" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="rowgroup" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="search" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="separator" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="status" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="table" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="tabpanel" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="term" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="timer" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="tooltip" onClick={() => {}} />;', errors: [expectedError] },
|
||||
// Handlers
|
||||
{ code: '<div role="article" onKeyDown={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onKeyPress={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onKeyUp={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onLoad={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onError={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseDown={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseUp={() => {}} />;', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = configs.recommended.rules[`jsx-a11y/${ruleName}`][1] || {};
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
// All the possible handlers
|
||||
{ code: '<div role="article" onCopy={() => {}} />;' },
|
||||
{ code: '<div role="article" onCut={() => {}} />;' },
|
||||
{ code: '<div role="article" onPaste={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onCompositionUpdate={() => {}} />;' },
|
||||
{ code: '<div role="article" onFocus={() => {}} />;' },
|
||||
{ code: '<div role="article" onBlur={() => {}} />;' },
|
||||
{ code: '<div role="article" onChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onInput={() => {}} />;' },
|
||||
{ code: '<div role="article" onSubmit={() => {}} />;' },
|
||||
{ code: '<div role="article" onContextMenu={() => {}} />;' },
|
||||
{ code: '<div role="article" onDblClick={() => {}} />;' },
|
||||
{ code: '<div role="article" onDoubleClick={() => {}} />;' },
|
||||
{ code: '<div role="article" onDrag={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragEnter={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragExit={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragLeave={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragOver={() => {}} />;' },
|
||||
{ code: '<div role="article" onDragStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onDrop={() => {}} />;' },
|
||||
{ code: '<div role="article" onMouseEnter={() => {}} />;' },
|
||||
{ code: '<div role="article" onMouseLeave={() => {}} />;' },
|
||||
{ code: '<div role="article" onMouseMove={() => {}} />;' },
|
||||
{ code: '<div role="article" onMouseOut={() => {}} />;' },
|
||||
{ code: '<div role="article" onMouseOver={() => {}} />;' },
|
||||
{ code: '<div role="article" onSelect={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchCancel={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchMove={() => {}} />;' },
|
||||
{ code: '<div role="article" onTouchStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onScroll={() => {}} />;' },
|
||||
{ code: '<div role="article" onWheel={() => {}} />;' },
|
||||
{ code: '<div role="article" onAbort={() => {}} />;' },
|
||||
{ code: '<div role="article" onCanPlay={() => {}} />;' },
|
||||
{ code: '<div role="article" onCanPlayThrough={() => {}} />;' },
|
||||
{ code: '<div role="article" onDurationChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onEmptied={() => {}} />;' },
|
||||
{ code: '<div role="article" onEncrypted={() => {}} />;' },
|
||||
{ code: '<div role="article" onEnded={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadedData={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadedMetadata={() => {}} />;' },
|
||||
{ code: '<div role="article" onLoadStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onPause={() => {}} />;' },
|
||||
{ code: '<div role="article" onPlay={() => {}} />;' },
|
||||
{ code: '<div role="article" onPlaying={() => {}} />;' },
|
||||
{ code: '<div role="article" onProgress={() => {}} />;' },
|
||||
{ code: '<div role="article" onRateChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onSeeked={() => {}} />;' },
|
||||
{ code: '<div role="article" onSeeking={() => {}} />;' },
|
||||
{ code: '<div role="article" onStalled={() => {}} />;' },
|
||||
{ code: '<div role="article" onSuspend={() => {}} />;' },
|
||||
{ code: '<div role="article" onTimeUpdate={() => {}} />;' },
|
||||
{ code: '<div role="article" onVolumeChange={() => {}} />;' },
|
||||
{ code: '<div role="article" onWaiting={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationStart={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationEnd={() => {}} />;' },
|
||||
{ code: '<div role="article" onAnimationIteration={() => {}} />;' },
|
||||
{ code: '<div role="article" onTransitionEnd={() => {}} />;' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
const strictOptions = configs.strict.rules[`jsx-a11y/${ruleName}`][1] || {};
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
// All the possible handlers
|
||||
{ code: '<div role="article" onFocus={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onBlur={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onContextMenu={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDblClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDoubleClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDrag={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragEnd={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragEnter={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragExit={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragLeave={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragOver={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDragStart={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onDrop={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseEnter={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseLeave={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseMove={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseOut={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div role="article" onMouseOver={() => {}} />;', errors: [expectedError] },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(strictOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
484
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js
generated
vendored
Normal file
484
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js
generated
vendored
Normal file
|
@ -0,0 +1,484 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Disallow inherently non-interactive elements to be assigned
|
||||
* interactive roles.
|
||||
* @author Jesse Beach
|
||||
* @author $AUTHOR
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-noninteractive-element-to-interactive-role';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = 'Non-interactive elements should not be assigned interactive roles.';
|
||||
|
||||
const expectedError = {
|
||||
message: errorMessage,
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
const ruleName = 'jsx-a11y/no-noninteractive-element-to-interactive-role';
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<Button onClick={doFoo} />' },
|
||||
/* Interactive elements */
|
||||
{ code: '<a tabIndex="0" role="button" />' },
|
||||
{ code: '<a href="http://x.y.z" role="button" />' },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="button" />' },
|
||||
{ code: '<area role="button" />;' },
|
||||
{ code: '<area role="menuitem" />;' },
|
||||
{ code: '<button className="foo" role="button" />' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="button" />' },
|
||||
{ code: '<input type="button" role="button" />' },
|
||||
{ code: '<input type="checkbox" role="button" />' },
|
||||
{ code: '<input type="color" role="button" />' },
|
||||
{ code: '<input type="date" role="button" />' },
|
||||
{ code: '<input type="datetime" role="button" />' },
|
||||
{ code: '<input type="datetime-local" role="button" />' },
|
||||
{ code: '<input type="email" role="button" />' },
|
||||
{ code: '<input type="file" role="button" />' },
|
||||
{ code: '<input type="hidden" role="button" />' },
|
||||
{ code: '<input type="image" role="button" />' },
|
||||
{ code: '<input type="month" role="button" />' },
|
||||
{ code: '<input type="number" role="button" />' },
|
||||
{ code: '<input type="password" role="button" />' },
|
||||
{ code: '<input type="radio" role="button" />' },
|
||||
{ code: '<input type="range" role="button" />' },
|
||||
{ code: '<input type="reset" role="button" />' },
|
||||
{ code: '<input type="search" role="button" />' },
|
||||
{ code: '<input type="submit" role="button" />' },
|
||||
{ code: '<input type="tel" role="button" />' },
|
||||
{ code: '<input type="text" role="button" />' },
|
||||
{ code: '<input type="time" role="button" />' },
|
||||
{ code: '<input type="url" role="button" />' },
|
||||
{ code: '<input type="week" role="button" />' },
|
||||
{ code: '<input type="hidden" role="img" />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="button" />;' },
|
||||
{ code: '<option className="foo" role="button" />' },
|
||||
{ code: '<select className="foo" role="button" />' },
|
||||
{ code: '<textarea className="foo" role="button" />' },
|
||||
{ code: '<tr role="button" />;' },
|
||||
{ code: '<tr role="presentation" />;' },
|
||||
/* Interactive elements */
|
||||
{ code: '<a tabIndex="0" role="img" />' },
|
||||
{ code: '<a href="http://x.y.z" role="img" />' },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="img" />' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="img" />' },
|
||||
{ code: '<input type="img" role="img" />' },
|
||||
{ code: '<input type="checkbox" role="img" />' },
|
||||
{ code: '<input type="color" role="img" />' },
|
||||
{ code: '<input type="date" role="img" />' },
|
||||
{ code: '<input type="datetime" role="img" />' },
|
||||
{ code: '<input type="datetime-local" role="img" />' },
|
||||
{ code: '<input type="email" role="img" />' },
|
||||
{ code: '<input type="file" role="img" />' },
|
||||
{ code: '<input type="hidden" role="button" />' },
|
||||
{ code: '<input type="image" role="img" />' },
|
||||
{ code: '<input type="month" role="img" />' },
|
||||
{ code: '<input type="number" role="img" />' },
|
||||
{ code: '<input type="password" role="img" />' },
|
||||
{ code: '<input type="radio" role="img" />' },
|
||||
{ code: '<input type="range" role="img" />' },
|
||||
{ code: '<input type="reset" role="img" />' },
|
||||
{ code: '<input type="search" role="img" />' },
|
||||
{ code: '<input type="submit" role="img" />' },
|
||||
{ code: '<input type="tel" role="img" />' },
|
||||
{ code: '<input type="text" role="img" />' },
|
||||
{ code: '<input type="time" role="img" />' },
|
||||
{ code: '<input type="url" role="img" />' },
|
||||
{ code: '<input type="week" role="img" />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="img" />;' },
|
||||
{ code: '<option className="foo" role="img" />' },
|
||||
{ code: '<select className="foo" role="img" />' },
|
||||
{ code: '<textarea className="foo" role="img" />' },
|
||||
{ code: '<tr role="img" />;' },
|
||||
/* Interactive elements */
|
||||
{ code: '<a tabIndex="0" role="listitem" />' },
|
||||
{ code: '<a href="http://x.y.z" role="listitem" />' },
|
||||
{ code: '<a href="http://x.y.z" tabIndex="0" role="listitem" />' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input role="listitem" />' },
|
||||
{ code: '<input type="listitem" role="listitem" />' },
|
||||
{ code: '<input type="checkbox" role="listitem" />' },
|
||||
{ code: '<input type="color" role="listitem" />' },
|
||||
{ code: '<input type="date" role="listitem" />' },
|
||||
{ code: '<input type="datetime" role="listitem" />' },
|
||||
{ code: '<input type="datetime-local" role="listitem" />' },
|
||||
{ code: '<input type="email" role="listitem" />' },
|
||||
{ code: '<input type="file" role="listitem" />' },
|
||||
{ code: '<input type="image" role="listitem" />' },
|
||||
{ code: '<input type="month" role="listitem" />' },
|
||||
{ code: '<input type="number" role="listitem" />' },
|
||||
{ code: '<input type="password" role="listitem" />' },
|
||||
{ code: '<input type="radio" role="listitem" />' },
|
||||
{ code: '<input type="range" role="listitem" />' },
|
||||
{ code: '<input type="reset" role="listitem" />' },
|
||||
{ code: '<input type="search" role="listitem" />' },
|
||||
{ code: '<input type="submit" role="listitem" />' },
|
||||
{ code: '<input type="tel" role="listitem" />' },
|
||||
{ code: '<input type="text" role="listitem" />' },
|
||||
{ code: '<input type="time" role="listitem" />' },
|
||||
{ code: '<input type="url" role="listitem" />' },
|
||||
{ code: '<input type="week" role="listitem" />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<menuitem role="listitem" />;' },
|
||||
{ code: '<option className="foo" role="listitem" />' },
|
||||
{ code: '<select className="foo" role="listitem" />' },
|
||||
{ code: '<textarea className="foo" role="listitem" />' },
|
||||
{ code: '<tr role="listitem" />;' },
|
||||
/* HTML elements with neither an interactive or non-interactive valence (static) */
|
||||
{ code: '<acronym role="button" />;' },
|
||||
{ code: '<address role="button" />;' },
|
||||
{ code: '<applet role="button" />;' },
|
||||
{ code: '<audio role="button" />;' },
|
||||
{ code: '<b role="button" />;' },
|
||||
{ code: '<base role="button" />;' },
|
||||
{ code: '<bdi role="button" />;' },
|
||||
{ code: '<bdo role="button" />;' },
|
||||
{ code: '<big role="button" />;' },
|
||||
{ code: '<blink role="button" />;' },
|
||||
{ code: '<canvas role="button" />;' },
|
||||
{ code: '<center role="button" />;' },
|
||||
{ code: '<cite role="button" />;' },
|
||||
{ code: '<code role="button" />;' },
|
||||
{ code: '<col role="button" />;' },
|
||||
{ code: '<colgroup role="button" />;' },
|
||||
{ code: '<content role="button" />;' },
|
||||
{ code: '<data role="button" />;' },
|
||||
{ code: '<datalist role="button" />;' },
|
||||
{ code: '<del role="button" />;' },
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div className="foo" role="button" />;' },
|
||||
{ code: '<div className="foo" {...props} role="button" />;' },
|
||||
{ code: '<div aria-hidden role="button" />;' },
|
||||
{ code: '<div aria-hidden={true} role="button" />;' },
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div role={undefined} role="button" />;' },
|
||||
{ code: '<div {...props} role="button" />;' },
|
||||
{ code: '<div onKeyUp={() => void 0} aria-hidden={false} role="button" />;' },
|
||||
{ code: '<em role="button" />;' },
|
||||
{ code: '<embed role="button" />;' },
|
||||
{ code: '<font role="button" />;' },
|
||||
{ code: '<frameset role="button" />;' },
|
||||
{ code: '<head role="button" />;' },
|
||||
{ code: '<header role="button" />;' },
|
||||
{ code: '<hgroup role="button" />;' },
|
||||
{ code: '<html role="button" />;' },
|
||||
{ code: '<i role="button" />;' },
|
||||
{ code: '<ins role="button" />;' },
|
||||
{ code: '<kbd role="button" />;' },
|
||||
{ code: '<keygen role="button" />;' },
|
||||
{ code: '<link role="button" />;' },
|
||||
{ code: '<map role="button" />;' },
|
||||
{ code: '<meta role="button" />;' },
|
||||
{ code: '<noembed role="button" />;' },
|
||||
{ code: '<noscript role="button" />;' },
|
||||
{ code: '<object role="button" />;' },
|
||||
{ code: '<param role="button" />;' },
|
||||
{ code: '<picture role="button" />;' },
|
||||
{ code: '<q role="button" />;' },
|
||||
{ code: '<rp role="button" />;' },
|
||||
{ code: '<rt role="button" />;' },
|
||||
{ code: '<rtc role="button" />;' },
|
||||
{ code: '<s role="button" />;' },
|
||||
{ code: '<samp role="button" />;' },
|
||||
{ code: '<script role="button" />;' },
|
||||
{ code: '<small role="button" />;' },
|
||||
{ code: '<source role="button" />;' },
|
||||
{ code: '<spacer role="button" />;' },
|
||||
{ code: '<span role="button" />;' },
|
||||
{ code: '<strike role="button" />;' },
|
||||
{ code: '<strong role="button" />;' },
|
||||
{ code: '<style role="button" />;' },
|
||||
{ code: '<sub role="button" />;' },
|
||||
{ code: '<summary role="button" />;' },
|
||||
{ code: '<sup role="button" />;' },
|
||||
{ code: '<th role="button" />;' },
|
||||
{ code: '<title role="button" />;' },
|
||||
{ code: '<track role="button" />;' },
|
||||
{ code: '<tt role="button" />;' },
|
||||
{ code: '<u role="button" />;' },
|
||||
{ code: '<var role="button" />;' },
|
||||
{ code: '<video role="button" />;' },
|
||||
{ code: '<wbr role="button" />;' },
|
||||
{ code: '<xmp role="button" />;' },
|
||||
/* HTML elements attributed with an interactive role */
|
||||
{ code: '<div role="button" />;' },
|
||||
{ code: '<div role="checkbox" />;' },
|
||||
{ code: '<div role="columnheader" />;' },
|
||||
{ code: '<div role="combobox" />;' },
|
||||
{ code: '<div role="grid" />;' },
|
||||
{ code: '<div role="gridcell" />;' },
|
||||
{ code: '<div role="link" />;' },
|
||||
{ code: '<div role="listbox" />;' },
|
||||
{ code: '<div role="menu" />;' },
|
||||
{ code: '<div role="menubar" />;' },
|
||||
{ code: '<div role="menuitem" />;' },
|
||||
{ code: '<div role="menuitemcheckbox" />;' },
|
||||
{ code: '<div role="menuitemradio" />;' },
|
||||
{ code: '<div role="option" />;' },
|
||||
{ code: '<div role="progressbar" />;' },
|
||||
{ code: '<div role="radio" />;' },
|
||||
{ code: '<div role="radiogroup" />;' },
|
||||
{ code: '<div role="row" />;' },
|
||||
{ code: '<div role="rowheader" />;' },
|
||||
{ code: '<div role="searchbox" />;' },
|
||||
{ code: '<div role="slider" />;' },
|
||||
{ code: '<div role="spinbutton" />;' },
|
||||
{ code: '<div role="switch" />;' },
|
||||
{ code: '<div role="tab" />;' },
|
||||
{ code: '<div role="textbox" />;' },
|
||||
{ code: '<div role="treeitem" />;' },
|
||||
/* Presentation is a special case role that indicates intentional static semantics */
|
||||
{ code: '<div role="presentation" />;' },
|
||||
/* HTML elements attributed with an abstract role */
|
||||
{ code: '<div role="command" />;' },
|
||||
{ code: '<div role="composite" />;' },
|
||||
{ code: '<div role="input" />;' },
|
||||
{ code: '<div role="landmark" />;' },
|
||||
{ code: '<div role="range" />;' },
|
||||
{ code: '<div role="roletype" />;' },
|
||||
{ code: '<div role="section" />;' },
|
||||
{ code: '<div role="sectionhead" />;' },
|
||||
{ code: '<div role="select" />;' },
|
||||
{ code: '<div role="structure" />;' },
|
||||
{ code: '<div role="tablist" />;' },
|
||||
{ code: '<div role="toolbar" />;' },
|
||||
{ code: '<div role="tree" />;' },
|
||||
{ code: '<div role="treegrid" />;' },
|
||||
{ code: '<div role="widget" />;' },
|
||||
{ code: '<div role="window" />;' },
|
||||
/* HTML elements with an inherent non-interactive role, assigned an
|
||||
* interactive role. */
|
||||
{ code: '<main role="listitem" />;' },
|
||||
{ code: '<a role="listitem" />' },
|
||||
{ code: '<a role="listitem" />;' },
|
||||
{ code: '<a role="button" />' },
|
||||
{ code: '<a role="button" />;' },
|
||||
{ code: '<a role="menuitem" />' },
|
||||
{ code: '<a role="menuitem" />;' },
|
||||
{ code: '<area role="listitem" />;' },
|
||||
{ code: '<article role="listitem" />;' },
|
||||
{ code: '<article role="listitem" />;' },
|
||||
{ code: '<dd role="listitem" />;' },
|
||||
{ code: '<dfn role="listitem" />;' },
|
||||
{ code: '<dt role="listitem" />;' },
|
||||
{ code: '<fieldset role="listitem" />;' },
|
||||
{ code: '<figure role="listitem" />;' },
|
||||
{ code: '<form role="listitem" />;' },
|
||||
{ code: '<frame role="listitem" />;' },
|
||||
{ code: '<h1 role="listitem" />;' },
|
||||
{ code: '<h2 role="listitem" />;' },
|
||||
{ code: '<h3 role="listitem" />;' },
|
||||
{ code: '<h4 role="listitem" />;' },
|
||||
{ code: '<h5 role="listitem" />;' },
|
||||
{ code: '<h6 role="listitem" />;' },
|
||||
{ code: '<hr role="listitem" />;' },
|
||||
{ code: '<img role="listitem" />;' },
|
||||
{ code: '<li role="listitem" />;' },
|
||||
{ code: '<li role="presentation" />;' },
|
||||
{ code: '<nav role="listitem" />;' },
|
||||
{ code: '<ol role="listitem" />;' },
|
||||
{ code: '<table role="listitem" />;' },
|
||||
{ code: '<tbody role="listitem" />;' },
|
||||
{ code: '<td role="listitem" />;' },
|
||||
{ code: '<tfoot role="listitem" />;' },
|
||||
{ code: '<thead role="listitem" />;' },
|
||||
{ code: '<ul role="listitem" />;' },
|
||||
/* HTML elements attributed with a non-interactive role */
|
||||
{ code: '<div role="alert" />;' },
|
||||
{ code: '<div role="alertdialog" />;' },
|
||||
{ code: '<div role="application" />;' },
|
||||
{ code: '<div role="article" />;' },
|
||||
{ code: '<div role="banner" />;' },
|
||||
{ code: '<div role="cell" />;' },
|
||||
{ code: '<div role="complementary" />;' },
|
||||
{ code: '<div role="contentinfo" />;' },
|
||||
{ code: '<div role="definition" />;' },
|
||||
{ code: '<div role="dialog" />;' },
|
||||
{ code: '<div role="directory" />;' },
|
||||
{ code: '<div role="document" />;' },
|
||||
{ code: '<div role="feed" />;' },
|
||||
{ code: '<div role="figure" />;' },
|
||||
{ code: '<div role="form" />;' },
|
||||
{ code: '<div role="group" />;' },
|
||||
{ code: '<div role="heading" />;' },
|
||||
{ code: '<div role="img" />;' },
|
||||
{ code: '<div role="list" />;' },
|
||||
{ code: '<div role="listitem" />;' },
|
||||
{ code: '<div role="log" />;' },
|
||||
{ code: '<div role="main" />;' },
|
||||
{ code: '<div role="marquee" />;' },
|
||||
{ code: '<div role="math" />;' },
|
||||
{ code: '<div role="navigation" />;' },
|
||||
{ code: '<div role="note" />;' },
|
||||
{ code: '<div role="region" />;' },
|
||||
{ code: '<div role="rowgroup" />;' },
|
||||
{ code: '<div role="search" />;' },
|
||||
{ code: '<div role="separator" />;' },
|
||||
{ code: '<div role="scrollbar" />;' },
|
||||
{ code: '<div role="status" />;' },
|
||||
{ code: '<div role="table" />;' },
|
||||
{ code: '<div role="tabpanel" />;' },
|
||||
{ code: '<div role="term" />;' },
|
||||
{ code: '<div role="timer" />;' },
|
||||
{ code: '<div role="tooltip" />;' },
|
||||
{ code: '<ul role="list" />;' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
/* HTML elements with an inherent non-interactive role, assigned an
|
||||
* interactive role. */
|
||||
{ code: '<main role="button" />;', errors: [expectedError] },
|
||||
{ code: '<article role="button" />;', errors: [expectedError] },
|
||||
{ code: '<article role="button" />;', errors: [expectedError] },
|
||||
{ code: '<aside role="button" />;', errors: [expectedError] },
|
||||
{ code: '<blockquote role="button" />;', errors: [expectedError] },
|
||||
{ code: '<body role="button" />;', errors: [expectedError] },
|
||||
{ code: '<br role="button" />;', errors: [expectedError] },
|
||||
{ code: '<caption role="button" />;', errors: [expectedError] },
|
||||
{ code: '<dd role="button" />;', errors: [expectedError] },
|
||||
{ code: '<details role="button" />;', errors: [expectedError] },
|
||||
{ code: '<dir role="button" />;', errors: [expectedError] },
|
||||
{ code: '<dl role="button" />;', errors: [expectedError] },
|
||||
{ code: '<dfn role="button" />;', errors: [expectedError] },
|
||||
{ code: '<dt role="button" />;', errors: [expectedError] },
|
||||
{ code: '<fieldset role="button" />;', errors: [expectedError] },
|
||||
{ code: '<figcaption role="button" />;', errors: [expectedError] },
|
||||
{ code: '<figure role="button" />;', errors: [expectedError] },
|
||||
{ code: '<footer role="button" />;', errors: [expectedError] },
|
||||
{ code: '<form role="button" />;', errors: [expectedError] },
|
||||
{ code: '<frame role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h1 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h2 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h3 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h4 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h5 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<h6 role="button" />;', errors: [expectedError] },
|
||||
{ code: '<hr role="button" />;', errors: [expectedError] },
|
||||
{ code: '<iframe role="button" />;', errors: [expectedError] },
|
||||
{ code: '<img role="button" />;', errors: [expectedError] },
|
||||
{ code: '<label role="button" />;', errors: [expectedError] },
|
||||
{ code: '<legend role="button" />;', errors: [expectedError] },
|
||||
{ code: '<li role="button" />;', errors: [expectedError] },
|
||||
{ code: '<mark role="button" />;', errors: [expectedError] },
|
||||
{ code: '<marquee role="button" />;', errors: [expectedError] },
|
||||
{ code: '<menu role="button" />;', errors: [expectedError] },
|
||||
{ code: '<meter role="button" />;', errors: [expectedError] },
|
||||
{ code: '<nav role="button" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="button" />;', errors: [expectedError] },
|
||||
{ code: '<optgroup role="button" />;', errors: [expectedError] },
|
||||
{ code: '<output role="button" />;', errors: [expectedError] },
|
||||
{ code: '<pre role="button" />;', errors: [expectedError] },
|
||||
{ code: '<progress role="button" />;', errors: [expectedError] },
|
||||
{ code: '<ruby role="button" />;', errors: [expectedError] },
|
||||
{ code: '<table role="button" />;', errors: [expectedError] },
|
||||
{ code: '<tbody role="button" />;', errors: [expectedError] },
|
||||
{ code: '<td role="button" />;', errors: [expectedError] },
|
||||
{ code: '<tfoot role="button" />;', errors: [expectedError] },
|
||||
{ code: '<thead role="button" />;', errors: [expectedError] },
|
||||
{ code: '<time role="button" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="button" />;', errors: [expectedError] },
|
||||
/* HTML elements with an inherent non-interactive role, assigned an
|
||||
* interactive role. */
|
||||
{ code: '<main role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<article role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<article role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<dd role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<dfn role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<dt role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<fieldset role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<figure role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<form role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<frame role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h1 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h2 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h3 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h4 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h5 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<h6 role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<hr role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<img role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<nav role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<p role="button" />;', errors: [expectedError] },
|
||||
{ code: '<section role="button" aria-label="Aardvark" />;', errors: [expectedError] },
|
||||
{ code: '<table role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<tbody role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<td role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<tfoot role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<thead role="menuitem" />;', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
{ code: '<ul role="menu" />;' },
|
||||
{ code: '<ul role="menubar" />;' },
|
||||
{ code: '<ul role="radiogroup" />;' },
|
||||
{ code: '<ul role="tablist" />;' },
|
||||
{ code: '<ul role="tree" />;' },
|
||||
{ code: '<ul role="treegrid" />;' },
|
||||
{ code: '<ol role="menu" />;' },
|
||||
{ code: '<ol role="menubar" />;' },
|
||||
{ code: '<ol role="radiogroup" />;' },
|
||||
{ code: '<ol role="tablist" />;' },
|
||||
{ code: '<ol role="tree" />;' },
|
||||
{ code: '<ol role="treegrid" />;' },
|
||||
{ code: '<li role="tab" />;' },
|
||||
{ code: '<li role="menuitem" />;' },
|
||||
{ code: '<li role="row" />;' },
|
||||
{ code: '<li role="treeitem" />;' },
|
||||
{ code: '<Component role="treeitem" />;' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
{ code: '<ul role="menu" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="menubar" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="radiogroup" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="tablist" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="tree" />;', errors: [expectedError] },
|
||||
{ code: '<ul role="treegrid" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="menu" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="menubar" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="radiogroup" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="tablist" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="tree" />;', errors: [expectedError] },
|
||||
{ code: '<ol role="treegrid" />;', errors: [expectedError] },
|
||||
{ code: '<li role="tab" />;', errors: [expectedError] },
|
||||
{ code: '<li role="menuitem" />;', errors: [expectedError] },
|
||||
{ code: '<li role="row" />;', errors: [expectedError] },
|
||||
{ code: '<li role="treeitem" />;', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-tabindex-test.js
generated
vendored
Normal file
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-noninteractive-tabindex-test.js
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Disallow tabindex on static and noninteractive elements
|
||||
* @author jessebeach
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-noninteractive-tabindex';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const ruleName = 'no-noninteractive-tabindex';
|
||||
|
||||
const expectedError = {
|
||||
message: '`tabIndex` should only be declared on interactive elements.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<MyButton tabIndex={0} />' },
|
||||
{ code: '<button />' },
|
||||
{ code: '<button tabIndex="0" />' },
|
||||
{ code: '<button tabIndex={0} />' },
|
||||
{ code: '<div />' },
|
||||
{ code: '<div tabIndex="-1" />' },
|
||||
{ code: '<div role="button" tabIndex="0" />' },
|
||||
{ code: '<div role="article" tabIndex="-1" />' },
|
||||
{ code: '<article tabIndex="-1" />' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
{ code: '<div tabIndex="0" />', errors: [expectedError] },
|
||||
{ code: '<div role="article" tabIndex="0" />', errors: [expectedError] },
|
||||
{ code: '<article tabIndex="0" />', errors: [expectedError] },
|
||||
{ code: '<article tabIndex={0} />', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = (
|
||||
configs.recommended.rules[`jsx-a11y/${ruleName}`][1] || {}
|
||||
);
|
||||
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
{ code: '<div role="tabpanel" tabIndex="0" />' },
|
||||
// Expressions should fail in strict mode
|
||||
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
{ code: '<div role="tabpanel" tabIndex="0" />', errors: [expectedError] },
|
||||
// Expressions should fail in strict mode
|
||||
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} tabIndex="0" />;', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-onchange-test.js
generated
vendored
Normal file
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-onchange-test.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce usage of onBlur over onChange on select menus for accessibility.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-onchange';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'onBlur must be used instead of onchange, unless absolutely necessary and it causes no negative consequences for keyboard only or screen reader users.',
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
ruleTester.run('no-onchange', rule, {
|
||||
valid: [
|
||||
{ code: '<select onBlur={() => {}} />;' },
|
||||
{ code: '<select onBlur={handleOnBlur} />;' },
|
||||
{ code: '<option />;' },
|
||||
{ code: '<option onBlur={() => {}} onChange={() => {}} />;' },
|
||||
{ code: '<option {...props} />' },
|
||||
{ code: '<input onChange={() => {}} />;' },
|
||||
{ code: '<input onChange={handleOnChange} />;' },
|
||||
{ code: '<input />;' },
|
||||
{ code: '<input onChange={() => {}} onChange={() => {}} />;' },
|
||||
{ code: '<input {...props} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<select onChange={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<select onChange={handleOnChange} />;', errors: [expectedError] },
|
||||
{ code: '<option onChange={() => {}} />', errors: [expectedError] },
|
||||
{ code: '<option onChange={() => {}} {...props} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-redundant-roles-test.js
generated
vendored
Normal file
79
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-redundant-roles-test.js
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce explicit role property is not the
|
||||
* same as implicit default role property on element.
|
||||
* @author Ethan Cohen <@evcohen>
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-redundant-roles';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = (element, implicitRole) => ({
|
||||
message: `The element ${element} has an implicit role of ${implicitRole}. Defining this explicitly is redundant and should be avoided.`,
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const ruleName = 'jsx-a11y/no-redundant-roles';
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<button role="main" />' },
|
||||
{ code: '<MyComponent role="button" />' },
|
||||
{ code: '<button role={`${foo}button`} />' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
{ code: '<button role="button" />', errors: [expectedError('button', 'button')] },
|
||||
{ code: '<body role="DOCUMENT" />', errors: [expectedError('body', 'document')] },
|
||||
];
|
||||
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
{ code: '<nav role="navigation" />' },
|
||||
]
|
||||
.map(parserOptionsMapper),
|
||||
invalid: neverValid
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
const noNavExceptionsOptions = { nav: [] };
|
||||
const listException = { ul: ['list'], ol: ['list'] };
|
||||
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: alwaysValid
|
||||
.map(ruleOptionsMapperFactory(noNavExceptionsOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
{ code: '<nav role="navigation" />', errors: [expectedError('nav', 'navigation')] },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(noNavExceptionsOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:recommended (valid list role override)`, rule, {
|
||||
valid: [
|
||||
{ code: '<ul role="list" />' },
|
||||
{ code: '<ol role="list" />' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(listException))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<ul role="list" />', errors: [expectedError('ul', 'list')] },
|
||||
{ code: '<ol role="list" />', errors: [expectedError('ol', 'list')] },
|
||||
]
|
||||
.map(parserOptionsMapper),
|
||||
});
|
458
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-static-element-interactions-test.js
generated
vendored
Normal file
458
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-static-element-interactions-test.js
generated
vendored
Normal file
|
@ -0,0 +1,458 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce static elements have no interactive handlers.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import { configs } from '../../../src/index';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/no-static-element-interactions';
|
||||
import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = 'Static HTML elements with event handlers require a role.';
|
||||
|
||||
const expectedError = {
|
||||
message: errorMessage,
|
||||
type: 'JSXOpeningElement',
|
||||
};
|
||||
|
||||
const ruleName = 'no-static-element-interactions';
|
||||
|
||||
const alwaysValid = [
|
||||
{ code: '<TestComponent onClick={doFoo} />' },
|
||||
{ code: '<Button onClick={doFoo} />' },
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div className="foo" />;' },
|
||||
{ code: '<div className="foo" {...props} />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden />;' },
|
||||
{ code: '<div onClick={() => void 0} aria-hidden={true} />;' },
|
||||
{ code: '<div onClick={null} />;' },
|
||||
/* All flavors of input */
|
||||
{ code: '<input onClick={() => void 0} />' },
|
||||
{ code: '<input type="button" onClick={() => void 0} />' },
|
||||
{ code: '<input type="checkbox" onClick={() => void 0} />' },
|
||||
{ code: '<input type="color" onClick={() => void 0} />' },
|
||||
{ code: '<input type="date" onClick={() => void 0} />' },
|
||||
{ code: '<input type="datetime" onClick={() => void 0} />' },
|
||||
{ code: '<input type="datetime-local" onClick={() => void 0} />' },
|
||||
{ code: '<input type="email" onClick={() => void 0} />' },
|
||||
{ code: '<input type="file" onClick={() => void 0} />' },
|
||||
{ code: '<input type="hidden" onClick={() => void 0} />' },
|
||||
{ code: '<input type="image" onClick={() => void 0} />' },
|
||||
{ code: '<input type="month" onClick={() => void 0} />' },
|
||||
{ code: '<input type="number" onClick={() => void 0} />' },
|
||||
{ code: '<input type="password" onClick={() => void 0} />' },
|
||||
{ code: '<input type="radio" onClick={() => void 0} />' },
|
||||
{ code: '<input type="range" onClick={() => void 0} />' },
|
||||
{ code: '<input type="reset" onClick={() => void 0} />' },
|
||||
{ code: '<input type="search" onClick={() => void 0} />' },
|
||||
{ code: '<input type="submit" onClick={() => void 0} />' },
|
||||
{ code: '<input type="tel" onClick={() => void 0} />' },
|
||||
{ code: '<input type="text" onClick={() => void 0} />' },
|
||||
{ code: '<input type="time" onClick={() => void 0} />' },
|
||||
{ code: '<input type="url" onClick={() => void 0} />' },
|
||||
{ code: '<input type="week" onClick={() => void 0} />' },
|
||||
/* End all flavors of input */
|
||||
{ code: '<button onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<datalist onClick={() => {}} />;' },
|
||||
{ code: '<menuitem onClick={() => {}} />;' },
|
||||
{ code: '<option onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<select onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<textarea onClick={() => void 0} className="foo" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" />' },
|
||||
{ code: '<a onClick={() => void 0} href="http://x.y.z" tabIndex="0" />' },
|
||||
{ code: '<audio onClick={() => {}} />;' },
|
||||
{ code: '<form onClick={() => {}} />;' },
|
||||
{ code: '<form onSubmit={() => {}} />;' },
|
||||
{ code: '<link onClick={() => {}} href="#" />;' },
|
||||
/* HTML elements attributed with an interactive role */
|
||||
{ code: '<div role="button" onClick={() => {}} />;' },
|
||||
{ code: '<div role="checkbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="columnheader" onClick={() => {}} />;' },
|
||||
{ code: '<div role="combobox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="form" onClick={() => {}} />;' },
|
||||
{ code: '<div role="gridcell" onClick={() => {}} />;' },
|
||||
{ code: '<div role="link" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitem" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitemcheckbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menuitemradio" onClick={() => {}} />;' },
|
||||
{ code: '<div role="option" onClick={() => {}} />;' },
|
||||
{ code: '<div role="radio" onClick={() => {}} />;' },
|
||||
{ code: '<div role="rowheader" onClick={() => {}} />;' },
|
||||
{ code: '<div role="searchbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="slider" onClick={() => {}} />;' },
|
||||
{ code: '<div role="spinbutton" onClick={() => {}} />;' },
|
||||
{ code: '<div role="switch" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tab" onClick={() => {}} />;' },
|
||||
{ code: '<div role="textbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="treeitem" onClick={() => {}} />;' },
|
||||
/* Presentation is a special case role that indicates intentional static semantics */
|
||||
{ code: '<div role="presentation" onClick={() => {}} />;' },
|
||||
{ code: '<div role="presentation" onKeyDown={() => {}} />;' },
|
||||
/* HTML elements with an inherent, non-interactive role */
|
||||
{ code: '<article onClick={() => {}} />;' },
|
||||
{ code: '<article onDblClick={() => void 0} />;' },
|
||||
{ code: '<aside onClick={() => {}} />;' },
|
||||
{ code: '<blockquote onClick={() => {}} />;' },
|
||||
{ code: '<body onClick={() => {}} />;' },
|
||||
{ code: '<br onClick={() => {}} />;' },
|
||||
{ code: '<canvas onClick={() => {}} />;' },
|
||||
{ code: '<caption onClick={() => {}} />;' },
|
||||
{ code: '<details onClick={() => {}} />;' },
|
||||
{ code: '<dd onClick={() => {}} />;' },
|
||||
{ code: '<dfn onClick={() => {}} />;' },
|
||||
{ code: '<dir onClick={() => {}} />;' },
|
||||
{ code: '<dl onClick={() => {}} />;' },
|
||||
{ code: '<dt onClick={() => {}} />;' },
|
||||
{ code: '<embed onClick={() => {}} />;' },
|
||||
{ code: '<fieldset onClick={() => {}} />;' },
|
||||
{ code: '<figcaption onClick={() => {}} />;' },
|
||||
{ code: '<figure onClick={() => {}} />;' },
|
||||
{ code: '<footer onClick={() => {}} />;' },
|
||||
{ code: '<frame onClick={() => {}} />;' },
|
||||
{ code: '<h1 onClick={() => {}} />;' },
|
||||
{ code: '<h2 onClick={() => {}} />;' },
|
||||
{ code: '<h3 onClick={() => {}} />;' },
|
||||
{ code: '<h4 onClick={() => {}} />;' },
|
||||
{ code: '<h5 onClick={() => {}} />;' },
|
||||
{ code: '<h6 onClick={() => {}} />;' },
|
||||
{ code: '<hr onClick={() => {}} />;' },
|
||||
{ code: '<iframe onClick={() => {}} />;' },
|
||||
{ code: '<img onClick={() => {}} />;' },
|
||||
{ code: '<label onClick={() => {}} />;' },
|
||||
{ code: '<legend onClick={() => {}} />;' },
|
||||
{ code: '<li onClick={() => {}} />;' },
|
||||
{ code: '<main onClick={() => void 0} />;' },
|
||||
{ code: '<mark onClick={() => {}} />;' },
|
||||
{ code: '<marquee onClick={() => {}} />;' },
|
||||
{ code: '<menu onClick={() => {}} />;' },
|
||||
{ code: '<meter onClick={() => {}} />;' },
|
||||
{ code: '<nav onClick={() => {}} />;' },
|
||||
{ code: '<ol onClick={() => {}} />;' },
|
||||
{ code: '<optgroup onClick={() => {}} />;' },
|
||||
{ code: '<output onClick={() => {}} />;' },
|
||||
{ code: '<p onClick={() => {}} />;' },
|
||||
{ code: '<pre onClick={() => {}} />;' },
|
||||
{ code: '<progress onClick={() => {}} />;' },
|
||||
{ code: '<ruby onClick={() => {}} />;' },
|
||||
{ code: '<section onClick={() => {}} aria-label="Aa" />;' },
|
||||
{ code: '<section onClick={() => {}} aria-labelledby="js_1" />;' },
|
||||
{ code: '<summary onClick={() => {}} />;' },
|
||||
{ code: '<table onClick={() => {}} />;' },
|
||||
{ code: '<tbody onClick={() => {}} />;' },
|
||||
{ code: '<tfoot onClick={() => {}} />;' },
|
||||
{ code: '<th onClick={() => {}} />;' },
|
||||
{ code: '<thead onClick={() => {}} />;' },
|
||||
{ code: '<time onClick={() => {}} />;' },
|
||||
{ code: '<tr onClick={() => {}} />;' },
|
||||
{ code: '<video onClick={() => {}} />;' },
|
||||
{ code: '<ul onClick={() => {}} />;' },
|
||||
/* HTML elements attributed with an abstract role */
|
||||
{ code: '<div role="command" onClick={() => {}} />;' },
|
||||
{ code: '<div role="composite" onClick={() => {}} />;' },
|
||||
{ code: '<div role="input" onClick={() => {}} />;' },
|
||||
{ code: '<div role="landmark" onClick={() => {}} />;' },
|
||||
{ code: '<div role="range" onClick={() => {}} />;' },
|
||||
{ code: '<div role="roletype" onClick={() => {}} />;' },
|
||||
{ code: '<div role="sectionhead" onClick={() => {}} />;' },
|
||||
{ code: '<div role="select" onClick={() => {}} />;' },
|
||||
{ code: '<div role="structure" onClick={() => {}} />;' },
|
||||
{ code: '<div role="widget" onClick={() => {}} />;' },
|
||||
{ code: '<div role="window" onClick={() => {}} />;' },
|
||||
/* HTML elements attributed with a non-interactive role */
|
||||
{ code: '<div role="alert" onClick={() => {}} />;' },
|
||||
{ code: '<div role="alertdialog" onClick={() => {}} />;' },
|
||||
{ code: '<div role="application" onClick={() => {}} />;' },
|
||||
{ code: '<div role="article" onClick={() => {}} />;' },
|
||||
{ code: '<div role="banner" onClick={() => {}} />;' },
|
||||
{ code: '<div role="cell" onClick={() => {}} />;' },
|
||||
{ code: '<div role="complementary" onClick={() => {}} />;' },
|
||||
{ code: '<div role="contentinfo" onClick={() => {}} />;' },
|
||||
{ code: '<div role="definition" onClick={() => {}} />;' },
|
||||
{ code: '<div role="dialog" onClick={() => {}} />;' },
|
||||
{ code: '<div role="directory" onClick={() => {}} />;' },
|
||||
{ code: '<div role="document" onClick={() => {}} />;' },
|
||||
{ code: '<div role="feed" onClick={() => {}} />;' },
|
||||
{ code: '<div role="figure" onClick={() => {}} />;' },
|
||||
{ code: '<div role="grid" onClick={() => {}} />;' },
|
||||
{ code: '<div role="group" onClick={() => {}} />;' },
|
||||
{ code: '<div role="heading" onClick={() => {}} />;' },
|
||||
{ code: '<div role="img" onClick={() => {}} />;' },
|
||||
{ code: '<div role="list" onClick={() => {}} />;' },
|
||||
{ code: '<div role="listbox" onClick={() => {}} />;' },
|
||||
{ code: '<div role="listitem" onClick={() => {}} />;' },
|
||||
{ code: '<div role="log" onClick={() => {}} />;' },
|
||||
{ code: '<div role="main" onClick={() => {}} />;' },
|
||||
{ code: '<div role="marquee" onClick={() => {}} />;' },
|
||||
{ code: '<div role="math" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menu" onClick={() => {}} />;' },
|
||||
{ code: '<div role="menubar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="navigation" onClick={() => {}} />;' },
|
||||
{ code: '<div role="note" onClick={() => {}} />;' },
|
||||
{ code: '<div role="progressbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="radiogroup" onClick={() => {}} />;' },
|
||||
{ code: '<div role="region" onClick={() => {}} />;' },
|
||||
{ code: '<div role="row" onClick={() => {}} />;' },
|
||||
{ code: '<div role="rowgroup" onClick={() => {}} />;' },
|
||||
{ code: '<div role="section" onClick={() => {}} />;' },
|
||||
{ code: '<div role="search" onClick={() => {}} />;' },
|
||||
{ code: '<div role="separator" onClick={() => {}} />;' },
|
||||
{ code: '<div role="scrollbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="status" onClick={() => {}} />;' },
|
||||
{ code: '<div role="table" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tablist" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tabpanel" onClick={() => {}} />;' },
|
||||
{ code: '<td onClick={() => {}} />;' },
|
||||
{ code: '<div role="term" onClick={() => {}} />;' },
|
||||
{ code: '<div role="timer" onClick={() => {}} />;' },
|
||||
{ code: '<div role="toolbar" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tooltip" onClick={() => {}} />;' },
|
||||
{ code: '<div role="tree" onClick={() => {}} />;' },
|
||||
{ code: '<div role="treegrid" onClick={() => {}} />;' },
|
||||
// All the possible handlers
|
||||
{ code: '<div onCopy={() => {}} />;' },
|
||||
{ code: '<div onCut={() => {}} />;' },
|
||||
{ code: '<div onPaste={() => {}} />;' },
|
||||
{ code: '<div onCompositionEnd={() => {}} />;' },
|
||||
{ code: '<div onCompositionStart={() => {}} />;' },
|
||||
{ code: '<div onCompositionUpdate={() => {}} />;' },
|
||||
{ code: '<div onChange={() => {}} />;' },
|
||||
{ code: '<div onInput={() => {}} />;' },
|
||||
{ code: '<div onSubmit={() => {}} />;' },
|
||||
{ code: '<div onSelect={() => {}} />;' },
|
||||
{ code: '<div onTouchCancel={() => {}} />;' },
|
||||
{ code: '<div onTouchEnd={() => {}} />;' },
|
||||
{ code: '<div onTouchMove={() => {}} />;' },
|
||||
{ code: '<div onTouchStart={() => {}} />;' },
|
||||
{ code: '<div onScroll={() => {}} />;' },
|
||||
{ code: '<div onWheel={() => {}} />;' },
|
||||
{ code: '<div onAbort={() => {}} />;' },
|
||||
{ code: '<div onCanPlay={() => {}} />;' },
|
||||
{ code: '<div onCanPlayThrough={() => {}} />;' },
|
||||
{ code: '<div onDurationChange={() => {}} />;' },
|
||||
{ code: '<div onEmptied={() => {}} />;' },
|
||||
{ code: '<div onEncrypted={() => {}} />;' },
|
||||
{ code: '<div onEnded={() => {}} />;' },
|
||||
{ code: '<div onError={() => {}} />;' },
|
||||
{ code: '<div onLoadedData={() => {}} />;' },
|
||||
{ code: '<div onLoadedMetadata={() => {}} />;' },
|
||||
{ code: '<div onLoadStart={() => {}} />;' },
|
||||
{ code: '<div onPause={() => {}} />;' },
|
||||
{ code: '<div onPlay={() => {}} />;' },
|
||||
{ code: '<div onPlaying={() => {}} />;' },
|
||||
{ code: '<div onProgress={() => {}} />;' },
|
||||
{ code: '<div onRateChange={() => {}} />;' },
|
||||
{ code: '<div onSeeked={() => {}} />;' },
|
||||
{ code: '<div onSeeking={() => {}} />;' },
|
||||
{ code: '<div onStalled={() => {}} />;' },
|
||||
{ code: '<div onSuspend={() => {}} />;' },
|
||||
{ code: '<div onTimeUpdate={() => {}} />;' },
|
||||
{ code: '<div onVolumeChange={() => {}} />;' },
|
||||
{ code: '<div onWaiting={() => {}} />;' },
|
||||
{ code: '<div onLoad={() => {}} />;' },
|
||||
{ code: '<div onError={() => {}} />;' },
|
||||
{ code: '<div onAnimationStart={() => {}} />;' },
|
||||
{ code: '<div onAnimationEnd={() => {}} />;' },
|
||||
{ code: '<div onAnimationIteration={() => {}} />;' },
|
||||
{ code: '<div onTransitionEnd={() => {}} />;' },
|
||||
];
|
||||
|
||||
const neverValid = [
|
||||
{ code: '<div onClick={() => void 0} />;', errors: [expectedError] },
|
||||
{ code: '<div onClick={() => void 0} role={undefined} />;', errors: [expectedError] },
|
||||
{ code: '<div onClick={() => void 0} {...props} />;', errors: [expectedError] },
|
||||
{ code: '<div onKeyUp={() => void 0} aria-hidden={false} />;', errors: [expectedError] },
|
||||
/* Static elements; no inherent role */
|
||||
{ code: '<a onClick={() => void 0} />', errors: [expectedError] },
|
||||
{ code: '<a onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<a tabIndex="0" onClick={() => void 0} />', errors: [expectedError] },
|
||||
{ code: '<area onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<acronym onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<address onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<applet onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<b onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<base onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<bdi onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<bdo onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<big onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<blink onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<center onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<cite onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<code onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<col onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<colgroup onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<content onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<data onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<del onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<em onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<font onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<frameset onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<head onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<header onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<hgroup onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<html onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<i onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<ins onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<kbd onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<keygen onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<map onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<meta onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<noembed onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<noscript onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<object onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<param onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<picture onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<q onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<rp onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<rt onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<rtc onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<s onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<samp onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<script onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<section onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<small onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<source onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<spacer onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<span onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<strike onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<strong onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<style onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<sub onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<sup onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<title onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<track onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<tt onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<u onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<var onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<wbr onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<xmp onClick={() => {}} />;', errors: [expectedError] },
|
||||
// Handlers
|
||||
{ code: '<div onKeyDown={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onKeyPress={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onKeyUp={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseDown={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseUp={() => {}} />;', errors: [expectedError] },
|
||||
];
|
||||
|
||||
const recommendedOptions = configs.recommended.rules[`jsx-a11y/${ruleName}`][1] || {};
|
||||
ruleTester.run(`${ruleName}:recommended`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
// All the possible handlers
|
||||
{ code: '<div onCopy={() => {}} />;' },
|
||||
{ code: '<div onCut={() => {}} />;' },
|
||||
{ code: '<div onPaste={() => {}} />;' },
|
||||
{ code: '<div onCompositionEnd={() => {}} />;' },
|
||||
{ code: '<div onCompositionStart={() => {}} />;' },
|
||||
{ code: '<div onCompositionUpdate={() => {}} />;' },
|
||||
{ code: '<div onFocus={() => {}} />;' },
|
||||
{ code: '<div onBlur={() => {}} />;' },
|
||||
{ code: '<div onChange={() => {}} />;' },
|
||||
{ code: '<div onInput={() => {}} />;' },
|
||||
{ code: '<div onSubmit={() => {}} />;' },
|
||||
{ code: '<div onContextMenu={() => {}} />;' },
|
||||
{ code: '<div onDblClick={() => {}} />;' },
|
||||
{ code: '<div onDoubleClick={() => {}} />;' },
|
||||
{ code: '<div onDrag={() => {}} />;' },
|
||||
{ code: '<div onDragEnd={() => {}} />;' },
|
||||
{ code: '<div onDragEnter={() => {}} />;' },
|
||||
{ code: '<div onDragExit={() => {}} />;' },
|
||||
{ code: '<div onDragLeave={() => {}} />;' },
|
||||
{ code: '<div onDragOver={() => {}} />;' },
|
||||
{ code: '<div onDragStart={() => {}} />;' },
|
||||
{ code: '<div onDrop={() => {}} />;' },
|
||||
{ code: '<div onMouseEnter={() => {}} />;' },
|
||||
{ code: '<div onMouseLeave={() => {}} />;' },
|
||||
{ code: '<div onMouseMove={() => {}} />;' },
|
||||
{ code: '<div onMouseOut={() => {}} />;' },
|
||||
{ code: '<div onMouseOver={() => {}} />;' },
|
||||
{ code: '<div onSelect={() => {}} />;' },
|
||||
{ code: '<div onTouchCancel={() => {}} />;' },
|
||||
{ code: '<div onTouchEnd={() => {}} />;' },
|
||||
{ code: '<div onTouchMove={() => {}} />;' },
|
||||
{ code: '<div onTouchStart={() => {}} />;' },
|
||||
{ code: '<div onScroll={() => {}} />;' },
|
||||
{ code: '<div onWheel={() => {}} />;' },
|
||||
{ code: '<div onAbort={() => {}} />;' },
|
||||
{ code: '<div onCanPlay={() => {}} />;' },
|
||||
{ code: '<div onCanPlayThrough={() => {}} />;' },
|
||||
{ code: '<div onDurationChange={() => {}} />;' },
|
||||
{ code: '<div onEmptied={() => {}} />;' },
|
||||
{ code: '<div onEncrypted={() => {}} />;' },
|
||||
{ code: '<div onEnded={() => {}} />;' },
|
||||
{ code: '<div onError={() => {}} />;' },
|
||||
{ code: '<div onLoadedData={() => {}} />;' },
|
||||
{ code: '<div onLoadedMetadata={() => {}} />;' },
|
||||
{ code: '<div onLoadStart={() => {}} />;' },
|
||||
{ code: '<div onPause={() => {}} />;' },
|
||||
{ code: '<div onPlay={() => {}} />;' },
|
||||
{ code: '<div onPlaying={() => {}} />;' },
|
||||
{ code: '<div onProgress={() => {}} />;' },
|
||||
{ code: '<div onRateChange={() => {}} />;' },
|
||||
{ code: '<div onSeeked={() => {}} />;' },
|
||||
{ code: '<div onSeeking={() => {}} />;' },
|
||||
{ code: '<div onStalled={() => {}} />;' },
|
||||
{ code: '<div onSuspend={() => {}} />;' },
|
||||
{ code: '<div onTimeUpdate={() => {}} />;' },
|
||||
{ code: '<div onVolumeChange={() => {}} />;' },
|
||||
{ code: '<div onWaiting={() => {}} />;' },
|
||||
{ code: '<div onLoad={() => {}} />;' },
|
||||
{ code: '<div onError={() => {}} />;' },
|
||||
{ code: '<div onAnimationStart={() => {}} />;' },
|
||||
{ code: '<div onAnimationEnd={() => {}} />;' },
|
||||
{ code: '<div onAnimationIteration={() => {}} />;' },
|
||||
{ code: '<div onTransitionEnd={() => {}} />;' },
|
||||
// Expressions should pass in recommended mode
|
||||
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;' },
|
||||
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>' },
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
]
|
||||
.map(ruleOptionsMapperFactory(recommendedOptions))
|
||||
.map(parserOptionsMapper),
|
||||
});
|
||||
|
||||
ruleTester.run(`${ruleName}:strict`, rule, {
|
||||
valid: [
|
||||
...alwaysValid,
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
...neverValid,
|
||||
// All the possible handlers
|
||||
{ code: '<div onContextMenu={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDblClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDoubleClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDrag={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragEnd={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragEnter={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragExit={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragLeave={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragOver={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDragStart={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onDrop={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseEnter={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseLeave={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseMove={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseOut={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div onMouseOver={() => {}} />;', errors: [expectedError] },
|
||||
// Expressions should fail in strict mode
|
||||
{ code: '<div role={ROLE_BUTTON} onClick={() => {}} />;', errors: [expectedError] },
|
||||
{ code: '<div {...this.props} role={this.props.role} onKeyPress={e => this.handleKeyPress(e)}>{this.props.children}</div>', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
120
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/role-has-required-aria-props-test.js
generated
vendored
Normal file
120
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/role-has-required-aria-props-test.js
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce that elements with ARIA roles must
|
||||
* have all required attributes for that role.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { roles } from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/role-has-required-aria-props';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const errorMessage = (role) => {
|
||||
const requiredProps = Object.keys(roles.get(role).requiredProps);
|
||||
|
||||
return {
|
||||
message: `Elements with the ARIA role "${role}" must have the following attributes defined: ${requiredProps}`,
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
};
|
||||
|
||||
// Create basic test cases using all valid role types.
|
||||
const basicValidityTests = [...roles.keys()].map((role) => {
|
||||
const {
|
||||
requiredProps: requiredPropKeyValues,
|
||||
} = roles.get(role);
|
||||
const requiredProps = Object.keys(requiredPropKeyValues);
|
||||
const propChain = requiredProps.join(' ');
|
||||
|
||||
return {
|
||||
code: `<div role="${role.toLowerCase()}" ${propChain} />`,
|
||||
};
|
||||
});
|
||||
|
||||
ruleTester.run('role-has-required-aria-props', rule, {
|
||||
valid: [
|
||||
{ code: '<Bar baz />' },
|
||||
{ code: '<MyComponent role="combobox" />' },
|
||||
// Variables should pass, as we are only testing literals.
|
||||
{ code: '<div />' },
|
||||
{ code: '<div></div>' },
|
||||
{ code: '<div role={role} />' },
|
||||
{ code: '<div role={role || "button"} />' },
|
||||
{ code: '<div role={role || "foobar"} />' },
|
||||
{ code: '<div role="row" />' },
|
||||
{ code: '<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>' },
|
||||
{ code: '<input role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0" {...props} type="checkbox" />' },
|
||||
{ code: '<input type="checkbox" role="switch" />' },
|
||||
].concat(basicValidityTests).map(parserOptionsMapper),
|
||||
|
||||
invalid: [
|
||||
// SLIDER
|
||||
{ code: '<div role="slider" />', errors: [errorMessage('slider')] },
|
||||
{
|
||||
code: '<div role="slider" aria-valuemax />',
|
||||
errors: [errorMessage('slider')],
|
||||
},
|
||||
{
|
||||
code: '<div role="slider" aria-valuemax aria-valuemin />',
|
||||
errors: [errorMessage('slider')],
|
||||
},
|
||||
|
||||
// CHECKBOX
|
||||
{ code: '<div role="checkbox" />', errors: [errorMessage('checkbox')] },
|
||||
{ code: '<div role="checkbox" checked />', errors: [errorMessage('checkbox')] },
|
||||
{
|
||||
code: '<div role="checkbox" aria-chcked />',
|
||||
errors: [errorMessage('checkbox')],
|
||||
},
|
||||
{
|
||||
code: '<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>',
|
||||
errors: [errorMessage('checkbox')],
|
||||
},
|
||||
|
||||
// COMBOBOX
|
||||
{ code: '<div role="combobox" />', errors: [errorMessage('combobox')] },
|
||||
{ code: '<div role="combobox" expanded />', errors: [errorMessage('combobox')] },
|
||||
{
|
||||
code: '<div role="combobox" aria-expandd />',
|
||||
errors: [errorMessage('combobox')],
|
||||
},
|
||||
|
||||
// SCROLLBAR
|
||||
{ code: '<div role="scrollbar" />', errors: [errorMessage('scrollbar')] },
|
||||
{
|
||||
code: '<div role="scrollbar" aria-valuemax />',
|
||||
errors: [errorMessage('scrollbar')],
|
||||
},
|
||||
{
|
||||
code: '<div role="scrollbar" aria-valuemax aria-valuemin />',
|
||||
errors: [errorMessage('scrollbar')],
|
||||
},
|
||||
{
|
||||
code: '<div role="scrollbar" aria-valuemax aria-valuenow />',
|
||||
errors: [errorMessage('scrollbar')],
|
||||
},
|
||||
{
|
||||
code: '<div role="scrollbar" aria-valuemin aria-valuenow />',
|
||||
errors: [errorMessage('scrollbar')],
|
||||
},
|
||||
{
|
||||
code: '<div role="heading" />',
|
||||
errors: [errorMessage('heading')],
|
||||
},
|
||||
{
|
||||
code: '<div role="option" />',
|
||||
errors: [errorMessage('option')],
|
||||
},
|
||||
].map(parserOptionsMapper),
|
||||
});
|
552
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/role-supports-aria-props-test.js
generated
vendored
Normal file
552
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/role-supports-aria-props-test.js
generated
vendored
Normal file
|
@ -0,0 +1,552 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce that an element does not have an unsupported ARIA attribute.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import {
|
||||
aria,
|
||||
roles,
|
||||
} from 'aria-query';
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/role-supports-aria-props';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const generateErrorMessage = (attr, role, tag, isImplicit) => {
|
||||
if (isImplicit) {
|
||||
return `The attribute ${attr} is not supported by the role ${role}. \
|
||||
This role is implicit on the element ${tag}.`;
|
||||
}
|
||||
|
||||
return `The attribute ${attr} is not supported by the role ${role}.`;
|
||||
};
|
||||
|
||||
const errorMessage = (attr, role, tag, isImplicit) => ({
|
||||
message: generateErrorMessage(attr, role, tag, isImplicit),
|
||||
type: 'JSXOpeningElement',
|
||||
});
|
||||
|
||||
const nonAbstractRoles = [...roles.keys()].filter((role) => roles.get(role).abstract === false);
|
||||
|
||||
const createTests = (rolesNames) => rolesNames.reduce((tests, role) => {
|
||||
const {
|
||||
props: propKeyValues,
|
||||
} = roles.get(role);
|
||||
const validPropsForRole = Object.keys(propKeyValues);
|
||||
const invalidPropsForRole = [...aria.keys()]
|
||||
.map((attribute) => attribute.toLowerCase())
|
||||
.filter((attribute) => validPropsForRole.indexOf(attribute) === -1);
|
||||
const normalRole = role.toLowerCase();
|
||||
|
||||
const allTests = [];
|
||||
|
||||
allTests[0] = tests[0].concat(validPropsForRole.map((prop) => ({
|
||||
code: `<div role="${normalRole}" ${prop.toLowerCase()} />`,
|
||||
})));
|
||||
|
||||
allTests[1] = tests[1].concat(invalidPropsForRole.map((prop) => ({
|
||||
code: `<div role="${normalRole}" ${prop.toLowerCase()} />`,
|
||||
errors: [errorMessage(prop.toLowerCase(), normalRole, 'div', false)],
|
||||
})));
|
||||
|
||||
return allTests;
|
||||
}, [[], []]);
|
||||
|
||||
const [validTests, invalidTests] = createTests(nonAbstractRoles);
|
||||
|
||||
ruleTester.run('role-supports-aria-props', rule, {
|
||||
valid: [
|
||||
{ code: '<Foo bar />' },
|
||||
{ code: '<div />' },
|
||||
{ code: '<div id="main" />' },
|
||||
{ code: '<div role />' },
|
||||
{ code: '<div role="presentation" {...props} />' },
|
||||
{ code: '<Foo.Bar baz={true} />' },
|
||||
|
||||
// IMPLICIT ROLE TESTS
|
||||
// A TESTS - implicit role is `link`
|
||||
{ code: '<a href="#" aria-expanded />' },
|
||||
{ code: '<a href="#" aria-atomic />' },
|
||||
{ code: '<a href="#" aria-busy />' },
|
||||
{ code: '<a href="#" aria-controls />' },
|
||||
{ code: '<a href="#" aria-current />' },
|
||||
{ code: '<a href="#" aria-describedby />' },
|
||||
{ code: '<a href="#" aria-disabled />' },
|
||||
{ code: '<a href="#" aria-dropeffect />' },
|
||||
{ code: '<a href="#" aria-flowto />' },
|
||||
{ code: '<a href="#" aria-grabbed />' },
|
||||
{ code: '<a href="#" aria-hidden />' },
|
||||
{ code: '<a href="#" aria-label />' },
|
||||
{ code: '<a href="#" aria-labelledby />' },
|
||||
{ code: '<a href="#" aria-live />' },
|
||||
{ code: '<a href="#" aria-owns />' },
|
||||
{ code: '<a href="#" aria-relevant />' },
|
||||
|
||||
// this will have global
|
||||
{ code: '<a aria-checked />' },
|
||||
|
||||
// AREA TESTS - implicit role is `link`
|
||||
{ code: '<area href="#" aria-expanded />' },
|
||||
{ code: '<area href="#" aria-atomic />' },
|
||||
{ code: '<area href="#" aria-busy />' },
|
||||
{ code: '<area href="#" aria-controls />' },
|
||||
{ code: '<area href="#" aria-describedby />' },
|
||||
{ code: '<area href="#" aria-disabled />' },
|
||||
{ code: '<area href="#" aria-dropeffect />' },
|
||||
{ code: '<area href="#" aria-flowto />' },
|
||||
{ code: '<area href="#" aria-grabbed />' },
|
||||
{ code: '<area href="#" aria-hidden />' },
|
||||
{ code: '<area href="#" aria-label />' },
|
||||
{ code: '<area href="#" aria-labelledby />' },
|
||||
{ code: '<area href="#" aria-live />' },
|
||||
{ code: '<area href="#" aria-owns />' },
|
||||
{ code: '<area href="#" aria-relevant />' },
|
||||
|
||||
// this will have global
|
||||
{ code: '<area aria-checked />' },
|
||||
|
||||
// LINK TESTS - implicit role is `link`
|
||||
{ code: '<link href="#" aria-expanded />' },
|
||||
{ code: '<link href="#" aria-atomic />' },
|
||||
{ code: '<link href="#" aria-busy />' },
|
||||
{ code: '<link href="#" aria-controls />' },
|
||||
{ code: '<link href="#" aria-describedby />' },
|
||||
{ code: '<link href="#" aria-disabled />' },
|
||||
{ code: '<link href="#" aria-dropeffect />' },
|
||||
{ code: '<link href="#" aria-flowto />' },
|
||||
{ code: '<link href="#" aria-grabbed />' },
|
||||
{ code: '<link href="#" aria-hidden />' },
|
||||
{ code: '<link href="#" aria-label />' },
|
||||
{ code: '<link href="#" aria-labelledby />' },
|
||||
{ code: '<link href="#" aria-live />' },
|
||||
{ code: '<link href="#" aria-owns />' },
|
||||
{ code: '<link href="#" aria-relevant />' },
|
||||
|
||||
// this will have global
|
||||
{ code: '<link aria-checked />' },
|
||||
|
||||
// IMG TESTS - no implicit role
|
||||
{ code: '<img alt="" aria-checked />' },
|
||||
|
||||
// this will have role of `img`
|
||||
{ code: '<img alt="foobar" aria-busy />' },
|
||||
|
||||
// MENU TESTS - implicit role is `toolbar` when `type="toolbar"`
|
||||
{ code: '<menu type="toolbar" aria-activedescendant />' },
|
||||
{ code: '<menu type="toolbar" aria-atomic />' },
|
||||
{ code: '<menu type="toolbar" aria-busy />' },
|
||||
{ code: '<menu type="toolbar" aria-controls />' },
|
||||
{ code: '<menu type="toolbar" aria-describedby />' },
|
||||
{ code: '<menu type="toolbar" aria-disabled />' },
|
||||
{ code: '<menu type="toolbar" aria-dropeffect />' },
|
||||
{ code: '<menu type="toolbar" aria-flowto />' },
|
||||
{ code: '<menu type="toolbar" aria-grabbed />' },
|
||||
{ code: '<menu type="toolbar" aria-hidden />' },
|
||||
{ code: '<menu type="toolbar" aria-label />' },
|
||||
{ code: '<menu type="toolbar" aria-labelledby />' },
|
||||
{ code: '<menu type="toolbar" aria-live />' },
|
||||
{ code: '<menu type="toolbar" aria-owns />' },
|
||||
{ code: '<menu type="toolbar" aria-relevant />' },
|
||||
|
||||
// this will have global
|
||||
{ code: '<menu aria-checked />' },
|
||||
|
||||
// MENUITEM TESTS
|
||||
// when `type="command`, the implicit role is `menuitem`
|
||||
{ code: '<menuitem type="command" aria-atomic />' },
|
||||
{ code: '<menuitem type="command" aria-busy />' },
|
||||
{ code: '<menuitem type="command" aria-controls />' },
|
||||
{ code: '<menuitem type="command" aria-describedby />' },
|
||||
{ code: '<menuitem type="command" aria-disabled />' },
|
||||
{ code: '<menuitem type="command" aria-dropeffect />' },
|
||||
{ code: '<menuitem type="command" aria-flowto />' },
|
||||
{ code: '<menuitem type="command" aria-grabbed />' },
|
||||
{ code: '<menuitem type="command" aria-haspopup />' },
|
||||
{ code: '<menuitem type="command" aria-hidden />' },
|
||||
{ code: '<menuitem type="command" aria-label />' },
|
||||
{ code: '<menuitem type="command" aria-labelledby />' },
|
||||
{ code: '<menuitem type="command" aria-live />' },
|
||||
{ code: '<menuitem type="command" aria-owns />' },
|
||||
{ code: '<menuitem type="command" aria-relevant />' },
|
||||
// when `type="checkbox`, the implicit role is `menuitemcheckbox`
|
||||
{ code: '<menuitem type="checkbox" aria-checked />' },
|
||||
{ code: '<menuitem type="checkbox" aria-atomic />' },
|
||||
{ code: '<menuitem type="checkbox" aria-busy />' },
|
||||
{ code: '<menuitem type="checkbox" aria-controls />' },
|
||||
{ code: '<menuitem type="checkbox" aria-describedby />' },
|
||||
{ code: '<menuitem type="checkbox" aria-disabled />' },
|
||||
{ code: '<menuitem type="checkbox" aria-dropeffect />' },
|
||||
{ code: '<menuitem type="checkbox" aria-flowto />' },
|
||||
{ code: '<menuitem type="checkbox" aria-grabbed />' },
|
||||
{ code: '<menuitem type="checkbox" aria-haspopup />' },
|
||||
{ code: '<menuitem type="checkbox" aria-hidden />' },
|
||||
{ code: '<menuitem type="checkbox" aria-invalid />' },
|
||||
{ code: '<menuitem type="checkbox" aria-label />' },
|
||||
{ code: '<menuitem type="checkbox" aria-labelledby />' },
|
||||
{ code: '<menuitem type="checkbox" aria-live />' },
|
||||
{ code: '<menuitem type="checkbox" aria-owns />' },
|
||||
{ code: '<menuitem type="checkbox" aria-relevant />' },
|
||||
// when `type="radio`, the implicit role is `menuitemradio`
|
||||
{ code: '<menuitem type="radio" aria-checked />' },
|
||||
{ code: '<menuitem type="radio" aria-atomic />' },
|
||||
{ code: '<menuitem type="radio" aria-busy />' },
|
||||
{ code: '<menuitem type="radio" aria-controls />' },
|
||||
{ code: '<menuitem type="radio" aria-describedby />' },
|
||||
{ code: '<menuitem type="radio" aria-disabled />' },
|
||||
{ code: '<menuitem type="radio" aria-dropeffect />' },
|
||||
{ code: '<menuitem type="radio" aria-flowto />' },
|
||||
{ code: '<menuitem type="radio" aria-grabbed />' },
|
||||
{ code: '<menuitem type="radio" aria-haspopup />' },
|
||||
{ code: '<menuitem type="radio" aria-hidden />' },
|
||||
{ code: '<menuitem type="radio" aria-invalid />' },
|
||||
{ code: '<menuitem type="radio" aria-label />' },
|
||||
{ code: '<menuitem type="radio" aria-labelledby />' },
|
||||
{ code: '<menuitem type="radio" aria-live />' },
|
||||
{ code: '<menuitem type="radio" aria-owns />' },
|
||||
{ code: '<menuitem type="radio" aria-relevant />' },
|
||||
{ code: '<menuitem type="radio" aria-posinset />' },
|
||||
{ code: '<menuitem type="radio" aria-setsize />' },
|
||||
|
||||
// these will have global
|
||||
{ code: '<menuitem aria-checked />' },
|
||||
{ code: '<menuitem type="foo" aria-checked />' },
|
||||
|
||||
// INPUT TESTS
|
||||
// when `type="button"`, the implicit role is `button`
|
||||
{ code: '<input type="button" aria-expanded />' },
|
||||
{ code: '<input type="button" aria-pressed />' },
|
||||
{ code: '<input type="button" aria-atomic />' },
|
||||
{ code: '<input type="button" aria-busy />' },
|
||||
{ code: '<input type="button" aria-controls />' },
|
||||
{ code: '<input type="button" aria-describedby />' },
|
||||
{ code: '<input type="button" aria-disabled />' },
|
||||
{ code: '<input type="button" aria-dropeffect />' },
|
||||
{ code: '<input type="button" aria-flowto />' },
|
||||
{ code: '<input type="button" aria-grabbed />' },
|
||||
{ code: '<input type="button" aria-haspopup />' },
|
||||
{ code: '<input type="button" aria-hidden />' },
|
||||
{ code: '<input type="button" aria-label />' },
|
||||
{ code: '<input type="button" aria-labelledby />' },
|
||||
{ code: '<input type="button" aria-live />' },
|
||||
{ code: '<input type="button" aria-owns />' },
|
||||
{ code: '<input type="button" aria-relevant />' },
|
||||
// when `type="image"`, the implicit role is `button`
|
||||
{ code: '<input type="image" aria-expanded />' },
|
||||
{ code: '<input type="image" aria-pressed />' },
|
||||
{ code: '<input type="image" aria-atomic />' },
|
||||
{ code: '<input type="image" aria-busy />' },
|
||||
{ code: '<input type="image" aria-controls />' },
|
||||
{ code: '<input type="image" aria-describedby />' },
|
||||
{ code: '<input type="image" aria-disabled />' },
|
||||
{ code: '<input type="image" aria-dropeffect />' },
|
||||
{ code: '<input type="image" aria-flowto />' },
|
||||
{ code: '<input type="image" aria-grabbed />' },
|
||||
{ code: '<input type="image" aria-haspopup />' },
|
||||
{ code: '<input type="image" aria-hidden />' },
|
||||
{ code: '<input type="image" aria-label />' },
|
||||
{ code: '<input type="image" aria-labelledby />' },
|
||||
{ code: '<input type="image" aria-live />' },
|
||||
{ code: '<input type="image" aria-owns />' },
|
||||
{ code: '<input type="image" aria-relevant />' },
|
||||
// when `type="reset"`, the implicit role is `button`
|
||||
{ code: '<input type="reset" aria-expanded />' },
|
||||
{ code: '<input type="reset" aria-pressed />' },
|
||||
{ code: '<input type="reset" aria-atomic />' },
|
||||
{ code: '<input type="reset" aria-busy />' },
|
||||
{ code: '<input type="reset" aria-controls />' },
|
||||
{ code: '<input type="reset" aria-describedby />' },
|
||||
{ code: '<input type="reset" aria-disabled />' },
|
||||
{ code: '<input type="reset" aria-dropeffect />' },
|
||||
{ code: '<input type="reset" aria-flowto />' },
|
||||
{ code: '<input type="reset" aria-grabbed />' },
|
||||
{ code: '<input type="reset" aria-haspopup />' },
|
||||
{ code: '<input type="reset" aria-hidden />' },
|
||||
{ code: '<input type="reset" aria-label />' },
|
||||
{ code: '<input type="reset" aria-labelledby />' },
|
||||
{ code: '<input type="reset" aria-live />' },
|
||||
{ code: '<input type="reset" aria-owns />' },
|
||||
{ code: '<input type="reset" aria-relevant />' },
|
||||
// when `type="submit"`, the implicit role is `button`
|
||||
{ code: '<input type="submit" aria-expanded />' },
|
||||
{ code: '<input type="submit" aria-pressed />' },
|
||||
{ code: '<input type="submit" aria-atomic />' },
|
||||
{ code: '<input type="submit" aria-busy />' },
|
||||
{ code: '<input type="submit" aria-controls />' },
|
||||
{ code: '<input type="submit" aria-describedby />' },
|
||||
{ code: '<input type="submit" aria-disabled />' },
|
||||
{ code: '<input type="submit" aria-dropeffect />' },
|
||||
{ code: '<input type="submit" aria-flowto />' },
|
||||
{ code: '<input type="submit" aria-grabbed />' },
|
||||
{ code: '<input type="submit" aria-haspopup />' },
|
||||
{ code: '<input type="submit" aria-hidden />' },
|
||||
{ code: '<input type="submit" aria-label />' },
|
||||
{ code: '<input type="submit" aria-labelledby />' },
|
||||
{ code: '<input type="submit" aria-live />' },
|
||||
{ code: '<input type="submit" aria-owns />' },
|
||||
{ code: '<input type="submit" aria-relevant />' },
|
||||
// when `type="checkbox"`, the implicit role is `checkbox`
|
||||
{ code: '<input type="checkbox" aria-atomic />' },
|
||||
{ code: '<input type="checkbox" aria-busy />' },
|
||||
{ code: '<input type="checkbox" aria-checked />' },
|
||||
{ code: '<input type="checkbox" aria-controls />' },
|
||||
{ code: '<input type="checkbox" aria-describedby />' },
|
||||
{ code: '<input type="checkbox" aria-disabled />' },
|
||||
{ code: '<input type="checkbox" aria-dropeffect />' },
|
||||
{ code: '<input type="checkbox" aria-flowto />' },
|
||||
{ code: '<input type="checkbox" aria-grabbed />' },
|
||||
{ code: '<input type="checkbox" aria-hidden />' },
|
||||
{ code: '<input type="checkbox" aria-invalid />' },
|
||||
{ code: '<input type="checkbox" aria-label />' },
|
||||
{ code: '<input type="checkbox" aria-labelledby />' },
|
||||
{ code: '<input type="checkbox" aria-live />' },
|
||||
{ code: '<input type="checkbox" aria-owns />' },
|
||||
{ code: '<input type="checkbox" aria-relevant />' },
|
||||
// when `type="radio"`, the implicit role is `radio`
|
||||
{ code: '<input type="radio" aria-atomic />' },
|
||||
{ code: '<input type="radio" aria-busy />' },
|
||||
{ code: '<input type="radio" aria-checked />' },
|
||||
{ code: '<input type="radio" aria-controls />' },
|
||||
{ code: '<input type="radio" aria-describedby />' },
|
||||
{ code: '<input type="radio" aria-disabled />' },
|
||||
{ code: '<input type="radio" aria-dropeffect />' },
|
||||
{ code: '<input type="radio" aria-flowto />' },
|
||||
{ code: '<input type="radio" aria-grabbed />' },
|
||||
{ code: '<input type="radio" aria-hidden />' },
|
||||
{ code: '<input type="radio" aria-label />' },
|
||||
{ code: '<input type="radio" aria-labelledby />' },
|
||||
{ code: '<input type="radio" aria-live />' },
|
||||
{ code: '<input type="radio" aria-owns />' },
|
||||
{ code: '<input type="radio" aria-relevant />' },
|
||||
{ code: '<input type="radio" aria-posinset />' },
|
||||
{ code: '<input type="radio" aria-setsize />' },
|
||||
// when `type="range"`, the implicit role is `slider`
|
||||
{ code: '<input type="range" aria-valuemax />' },
|
||||
{ code: '<input type="range" aria-valuemin />' },
|
||||
{ code: '<input type="range" aria-valuenow />' },
|
||||
{ code: '<input type="range" aria-orientation />' },
|
||||
{ code: '<input type="range" aria-atomic />' },
|
||||
{ code: '<input type="range" aria-busy />' },
|
||||
{ code: '<input type="range" aria-controls />' },
|
||||
{ code: '<input type="range" aria-describedby />' },
|
||||
{ code: '<input type="range" aria-disabled />' },
|
||||
{ code: '<input type="range" aria-dropeffect />' },
|
||||
{ code: '<input type="range" aria-flowto />' },
|
||||
{ code: '<input type="range" aria-grabbed />' },
|
||||
{ code: '<input type="range" aria-haspopup />' },
|
||||
{ code: '<input type="range" aria-hidden />' },
|
||||
{ code: '<input type="range" aria-invalid />' },
|
||||
{ code: '<input type="range" aria-label />' },
|
||||
{ code: '<input type="range" aria-labelledby />' },
|
||||
{ code: '<input type="range" aria-live />' },
|
||||
{ code: '<input type="range" aria-owns />' },
|
||||
{ code: '<input type="range" aria-relevant />' },
|
||||
{ code: '<input type="range" aria-valuetext />' },
|
||||
|
||||
// these will have role of `textbox`,
|
||||
{ code: '<input type="email" aria-disabled />' },
|
||||
{ code: '<input type="password" aria-disabled />' },
|
||||
{ code: '<input type="search" aria-disabled />' },
|
||||
{ code: '<input type="tel" aria-disabled />' },
|
||||
{ code: '<input type="url" aria-disabled />' },
|
||||
{ code: '<input aria-disabled />' },
|
||||
|
||||
// Allow null/undefined values regardless of role
|
||||
{ code: '<h2 role="presentation" aria-level={null} />' },
|
||||
{ code: '<h2 role="presentation" aria-level={undefined} />' },
|
||||
|
||||
// OTHER TESTS
|
||||
{ code: '<button aria-pressed />' },
|
||||
{ code: '<form aria-hidden />' },
|
||||
{ code: '<h1 aria-hidden />' },
|
||||
{ code: '<h2 aria-hidden />' },
|
||||
{ code: '<h3 aria-hidden />' },
|
||||
{ code: '<h4 aria-hidden />' },
|
||||
{ code: '<h5 aria-hidden />' },
|
||||
{ code: '<h6 aria-hidden />' },
|
||||
{ code: '<hr aria-hidden />' },
|
||||
{ code: '<li aria-current />' },
|
||||
{ code: '<meter aria-atomic />' },
|
||||
{ code: '<option aria-atomic />' },
|
||||
{ code: '<progress aria-atomic />' },
|
||||
{ code: '<textarea aria-hidden />' },
|
||||
{ code: '<select aria-expanded />' },
|
||||
{ code: '<datalist aria-expanded />' },
|
||||
{ code: '<div role="heading" aria-level />' },
|
||||
{ code: '<div role="heading" aria-level="1" />' },
|
||||
|
||||
].concat(validTests).map(parserOptionsMapper),
|
||||
|
||||
invalid: [
|
||||
// implicit basic checks
|
||||
{
|
||||
code: '<a href="#" aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'link', 'a', true)],
|
||||
},
|
||||
{
|
||||
code: '<area href="#" aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'link', 'area', true)],
|
||||
},
|
||||
{
|
||||
code: '<link href="#" aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'link', 'link', true)],
|
||||
},
|
||||
{
|
||||
code: '<img alt="foobar" aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'img', 'img', true)],
|
||||
},
|
||||
{
|
||||
code: '<menu type="toolbar" aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'toolbar', 'menu', true)],
|
||||
},
|
||||
{
|
||||
code: '<aside aria-checked />',
|
||||
errors: [errorMessage('aria-checked', 'complementary', 'aside', true)],
|
||||
},
|
||||
{
|
||||
code: '<ul aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'list', 'ul', true)],
|
||||
},
|
||||
{
|
||||
code: '<details aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'group', 'details', true)],
|
||||
},
|
||||
{
|
||||
code: '<dialog aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'dialog', 'dialog', true)],
|
||||
},
|
||||
{
|
||||
code: '<dl aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'list', 'dl', true)],
|
||||
},
|
||||
{
|
||||
code: '<aside aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'complementary', 'aside', true)],
|
||||
},
|
||||
{
|
||||
code: '<article aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'article', 'article', true)],
|
||||
},
|
||||
{
|
||||
code: '<body aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'document', 'body', true)],
|
||||
},
|
||||
{
|
||||
code: '<li aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'listitem', 'li', true)],
|
||||
},
|
||||
{
|
||||
code: '<nav aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'navigation', 'nav', true)],
|
||||
},
|
||||
{
|
||||
code: '<ol aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'list', 'ol', true)],
|
||||
},
|
||||
{
|
||||
code: '<output aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'status', 'output', true)],
|
||||
},
|
||||
{
|
||||
code: '<section aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'region', 'section', true)],
|
||||
},
|
||||
{
|
||||
code: '<tbody aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'rowgroup', 'tbody', true)],
|
||||
},
|
||||
{
|
||||
code: '<tfoot aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'rowgroup', 'tfoot', true)],
|
||||
},
|
||||
{
|
||||
code: '<thead aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'rowgroup', 'thead', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="radio" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'radio', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="radio" aria-selected />',
|
||||
errors: [errorMessage('aria-selected', 'radio', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="radio" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'radio', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="checkbox" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'checkbox', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="reset" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'button', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="submit" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'button', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="image" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'button', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<input type="button" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'button', 'input', true)],
|
||||
},
|
||||
{
|
||||
code: '<menuitem type="command" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'menuitem', 'menuitem', true)],
|
||||
},
|
||||
{
|
||||
code: '<menuitem type="radio" aria-selected />',
|
||||
errors: [errorMessage('aria-selected', 'menuitemradio', 'menuitem', true)],
|
||||
},
|
||||
{
|
||||
code: '<menu type="toolbar" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'toolbar', 'menu', true)],
|
||||
},
|
||||
{
|
||||
code: '<menu type="toolbar" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'toolbar', 'menu', true)],
|
||||
},
|
||||
{
|
||||
code: '<menu type="toolbar" aria-expanded />',
|
||||
errors: [errorMessage('aria-expanded', 'toolbar', 'menu', true)],
|
||||
},
|
||||
{
|
||||
code: '<link href="#" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'link', 'link', true)],
|
||||
},
|
||||
{
|
||||
code: '<link href="#" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'link', 'link', true)],
|
||||
},
|
||||
{
|
||||
code: '<area href="#" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'link', 'area', true)],
|
||||
},
|
||||
{
|
||||
code: '<area href="#" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'link', 'area', true)],
|
||||
},
|
||||
{
|
||||
code: '<a href="#" aria-haspopup />',
|
||||
errors: [errorMessage('aria-haspopup', 'link', 'a', true)],
|
||||
},
|
||||
{
|
||||
code: '<a href="#" aria-invalid />',
|
||||
errors: [errorMessage('aria-invalid', 'link', 'a', true)],
|
||||
},
|
||||
].concat(invalidTests).map(parserOptionsMapper),
|
||||
});
|
39
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/scope-test.js
generated
vendored
Normal file
39
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/scope-test.js
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce scope prop is only used on <th> elements.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/scope';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'The scope prop can only be used on <th> elements.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
ruleTester.run('scope', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div foo />;' },
|
||||
{ code: '<th scope />' },
|
||||
{ code: '<th scope="row" />' },
|
||||
{ code: '<th scope={foo} />' },
|
||||
{ code: '<th scope={"col"} {...props} />' },
|
||||
{ code: '<Foo scope="bar" {...props} />' },
|
||||
].map(parserOptionsMapper),
|
||||
invalid: [
|
||||
{ code: '<div scope />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
55
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/tabindex-no-positive-test.js
generated
vendored
Normal file
55
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/tabindex-no-positive-test.js
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* eslint-env jest */
|
||||
/**
|
||||
* @fileoverview Enforce tabIndex value is not greater than zero.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
import { RuleTester } from 'eslint';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
import rule from '../../../src/rules/tabindex-no-positive';
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
const expectedError = {
|
||||
message: 'Avoid positive integer values for tabIndex.',
|
||||
type: 'JSXAttribute',
|
||||
};
|
||||
|
||||
ruleTester.run('tabindex-no-positive', rule, {
|
||||
valid: [
|
||||
{ code: '<div />;' },
|
||||
{ code: '<div {...props} />' },
|
||||
{ code: '<div id="main" />' },
|
||||
{ code: '<div tabIndex={undefined} />' },
|
||||
{ code: '<div tabIndex={`${undefined}`} />' },
|
||||
{ code: '<div tabIndex={`${undefined}${undefined}`} />' },
|
||||
{ code: '<div tabIndex={0} />' },
|
||||
{ code: '<div tabIndex={-1} />' },
|
||||
{ code: '<div tabIndex={null} />' },
|
||||
{ code: '<div tabIndex={bar()} />' },
|
||||
{ code: '<div tabIndex={bar} />' },
|
||||
{ code: '<div tabIndex={"foobar"} />' },
|
||||
{ code: '<div tabIndex="0" />' },
|
||||
{ code: '<div tabIndex="-1" />' },
|
||||
{ code: '<div tabIndex="-5" />' },
|
||||
{ code: '<div tabIndex="-5.5" />' },
|
||||
{ code: '<div tabIndex={-5.5} />' },
|
||||
{ code: '<div tabIndex={-5} />' },
|
||||
].map(parserOptionsMapper),
|
||||
|
||||
invalid: [
|
||||
{ code: '<div tabIndex="1" />', errors: [expectedError] },
|
||||
{ code: '<div tabIndex={1} />', errors: [expectedError] },
|
||||
{ code: '<div tabIndex={"1"} />', errors: [expectedError] },
|
||||
{ code: '<div tabIndex={`1`} />', errors: [expectedError] },
|
||||
{ code: '<div tabIndex={1.589} />', errors: [expectedError] },
|
||||
].map(parserOptionsMapper),
|
||||
});
|
116
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/attributesComparator-test.js
generated
vendored
Normal file
116
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/attributesComparator-test.js
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import attributesComparator from '../../../src/util/attributesComparator';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
|
||||
describe('attributesComparator', () => {
|
||||
describe('base attributes', () => {
|
||||
let baseAttributes;
|
||||
let attributes;
|
||||
describe('are undefined', () => {
|
||||
describe('and attributes are undefined', () => {
|
||||
it('should return true', () => {
|
||||
expect(attributesComparator()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('are empty', () => {
|
||||
beforeEach(() => {
|
||||
baseAttributes = [];
|
||||
});
|
||||
describe('and attributes', () => {
|
||||
describe('are empty', () => {
|
||||
attributes = [];
|
||||
it('should return true', () => {
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
describe('have values', () => {
|
||||
attributes = [
|
||||
JSXAttributeMock('foo', 0),
|
||||
JSXAttributeMock('bar', 'baz'),
|
||||
];
|
||||
it('should return true', () => {
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('have values', () => {
|
||||
beforeEach(() => {
|
||||
baseAttributes = [
|
||||
{
|
||||
name: 'biz',
|
||||
value: 1,
|
||||
}, {
|
||||
name: 'fizz',
|
||||
value: 'pop',
|
||||
}, {
|
||||
name: 'fuzz',
|
||||
value: 'lolz',
|
||||
},
|
||||
];
|
||||
});
|
||||
describe('and attributes', () => {
|
||||
describe('are empty', () => {
|
||||
attributes = [];
|
||||
it('should return false', () => {
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('have values', () => {
|
||||
describe('and the values are the different', () => {
|
||||
it('should return false', () => {
|
||||
attributes = [
|
||||
JSXElementMock(),
|
||||
JSXAttributeMock('biz', 2),
|
||||
JSXAttributeMock('ziff', 'opo'),
|
||||
JSXAttributeMock('far', 'lolz'),
|
||||
];
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('and the values are a subset', () => {
|
||||
it('should return true', () => {
|
||||
attributes = [
|
||||
JSXAttributeMock('biz', 1),
|
||||
JSXAttributeMock('fizz', 'pop'),
|
||||
JSXAttributeMock('goo', 'gazz'),
|
||||
];
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('and the values are the same', () => {
|
||||
it('should return true', () => {
|
||||
attributes = [
|
||||
JSXAttributeMock('biz', 1),
|
||||
JSXAttributeMock('fizz', 'pop'),
|
||||
JSXAttributeMock('fuzz', 'lolz'),
|
||||
];
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
describe('and the values are a superset', () => {
|
||||
it('should return true', () => {
|
||||
attributes = [
|
||||
JSXAttributeMock('biz', 1),
|
||||
JSXAttributeMock('fizz', 'pop'),
|
||||
JSXAttributeMock('fuzz', 'lolz'),
|
||||
JSXAttributeMock('dar', 'tee'),
|
||||
];
|
||||
expect(attributesComparator(baseAttributes, attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getComputedRole-test.js
generated
vendored
Normal file
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getComputedRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* eslint-env jest */
|
||||
import getComputedRole from '../../../src/util/getComputedRole';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
|
||||
describe('getComputedRole', () => {
|
||||
describe('explicit role', () => {
|
||||
describe('valid role', () => {
|
||||
it('should return the role', () => {
|
||||
expect(getComputedRole(
|
||||
'div',
|
||||
[JSXAttributeMock('role', 'button')],
|
||||
)).toBe('button');
|
||||
});
|
||||
});
|
||||
describe('invalid role', () => {
|
||||
describe('has implicit', () => {
|
||||
it('should return the implicit role', () => {
|
||||
expect(getComputedRole(
|
||||
'li',
|
||||
[JSXAttributeMock('role', 'beeswax')],
|
||||
)).toBe('listitem');
|
||||
});
|
||||
});
|
||||
describe('lacks implicit', () => {
|
||||
it('should return null', () => {
|
||||
expect(getComputedRole(
|
||||
'div',
|
||||
[JSXAttributeMock('role', 'beeswax')],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('no role', () => {
|
||||
describe('has implicit', () => {
|
||||
it('should return the implicit role', () => {
|
||||
expect(getComputedRole(
|
||||
'li',
|
||||
[],
|
||||
)).toBe('listitem');
|
||||
});
|
||||
});
|
||||
describe('lacks implicit', () => {
|
||||
it('should return null', () => {
|
||||
expect(getComputedRole(
|
||||
'div',
|
||||
[],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('implicit role', () => {
|
||||
describe('has implicit', () => {
|
||||
it('should return the implicit role', () => {
|
||||
expect(getComputedRole(
|
||||
'li',
|
||||
[JSXAttributeMock('role', 'beeswax')],
|
||||
)).toBe('listitem');
|
||||
});
|
||||
});
|
||||
describe('lacks implicit', () => {
|
||||
it('should return null', () => {
|
||||
expect(getComputedRole(
|
||||
'div',
|
||||
[],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
30
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getExplicitRole-test.js
generated
vendored
Normal file
30
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getExplicitRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* eslint-env jest */
|
||||
import getExplicitRole from '../../../src/util/getExplicitRole';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
|
||||
describe('getExplicitRole', () => {
|
||||
describe('valid role', () => {
|
||||
it('should return the role', () => {
|
||||
expect(getExplicitRole(
|
||||
'div',
|
||||
[JSXAttributeMock('role', 'button')],
|
||||
)).toBe('button');
|
||||
});
|
||||
});
|
||||
describe('invalid role', () => {
|
||||
it('should return null', () => {
|
||||
expect(getExplicitRole(
|
||||
'div',
|
||||
[JSXAttributeMock('role', 'beeswax')],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
describe('no role', () => {
|
||||
it('should return null', () => {
|
||||
expect(getExplicitRole(
|
||||
'div',
|
||||
[],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
21
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getImplicitRole-test.js
generated
vendored
Normal file
21
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getImplicitRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* eslint-env jest */
|
||||
import getImplicitRole from '../../../src/util/getImplicitRole';
|
||||
|
||||
describe('getImplicitRole', () => {
|
||||
describe('has implicit', () => {
|
||||
it('should return the implicit role', () => {
|
||||
expect(getImplicitRole(
|
||||
'li',
|
||||
[],
|
||||
)).toBe('listitem');
|
||||
});
|
||||
});
|
||||
describe('lacks implicit', () => {
|
||||
it('should return null', () => {
|
||||
expect(getImplicitRole(
|
||||
'div',
|
||||
[],
|
||||
)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
49
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getSuggestion-test.js
generated
vendored
Normal file
49
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getSuggestion-test.js
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* eslint-env jest */
|
||||
import assert from 'assert';
|
||||
import getSuggestion from '../../../src/util/getSuggestion';
|
||||
|
||||
describe('spell check suggestion API', () => {
|
||||
it('should return no suggestions given empty word and no dictionary', () => {
|
||||
const word = '';
|
||||
const expected = [];
|
||||
const actual = getSuggestion(word);
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
it('should return no suggestions given real word and no dictionary', () => {
|
||||
const word = 'foo';
|
||||
const expected = [];
|
||||
const actual = getSuggestion(word);
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
it('should return correct suggestion given real word and a dictionary', () => {
|
||||
const word = 'fo';
|
||||
const dictionary = ['foo', 'bar', 'baz'];
|
||||
const expected = ['foo'];
|
||||
const actual = getSuggestion(word, dictionary);
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
it('should return multiple correct suggestions given real word and a dictionary', () => {
|
||||
const word = 'theer';
|
||||
const dictionary = ['there', 'their', 'foo', 'bar'];
|
||||
const expected = ['there', 'their'];
|
||||
const actual = getSuggestion(word, dictionary);
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
|
||||
it('should return correct # of suggestions given the limit argument', () => {
|
||||
const word = 'theer';
|
||||
const dictionary = ['there', 'their', 'foo', 'bar'];
|
||||
const limit = 1;
|
||||
const expected = 1;
|
||||
const actual = getSuggestion(word, dictionary, limit).length;
|
||||
|
||||
assert.deepEqual(expected, actual);
|
||||
});
|
||||
});
|
83
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getTabIndex-test.js
generated
vendored
Normal file
83
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/getTabIndex-test.js
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* eslint-env jest */
|
||||
import getTabIndex from '../../../src/util/getTabIndex';
|
||||
import IdentifierMock from '../../../__mocks__/IdentifierMock';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
|
||||
describe('getTabIndex', () => {
|
||||
describe('tabIndex is defined', () => {
|
||||
describe('as a number ', () => {
|
||||
describe('zero', () => {
|
||||
it('should return zero', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', 0))).toBe(0);
|
||||
});
|
||||
});
|
||||
describe('positive integer', () => {
|
||||
it('should return the integer', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', 1))).toBe(1);
|
||||
});
|
||||
});
|
||||
describe('negative integer', () => {
|
||||
it('should return the integer', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', -1))).toBe(-1);
|
||||
});
|
||||
});
|
||||
describe('float', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', 9.1))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('as a string', () => {
|
||||
describe('empty', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', ''))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
describe('which converts to a number', () => {
|
||||
it('should return an integer', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', '0'))).toBe(0);
|
||||
});
|
||||
});
|
||||
describe('which is NaN', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', '0a'))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('as a boolean', () => {
|
||||
describe('true', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', true))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
describe('false', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', false))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('as an expression', () => {
|
||||
describe('function expression', () => {
|
||||
it('should return the correct type', () => {
|
||||
const attr = function mockFn() { return 0; };
|
||||
expect(typeof getTabIndex(JSXAttributeMock('tabIndex', attr))).toEqual('function');
|
||||
});
|
||||
});
|
||||
describe('variable expression', () => {
|
||||
it('should return the Identifier name', () => {
|
||||
const name = 'identName';
|
||||
expect(getTabIndex(JSXAttributeMock(
|
||||
'tabIndex',
|
||||
IdentifierMock(name),
|
||||
true,
|
||||
))).toEqual(name);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('tabIndex is not defined', () => {
|
||||
it('should return undefined', () => {
|
||||
expect(getTabIndex(JSXAttributeMock('tabIndex', undefined))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
88
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/hasAccessibleChild-test.js
generated
vendored
Normal file
88
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/hasAccessibleChild-test.js
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
/* eslint-env jest */
|
||||
import hasAccessibleChild from '../../../src/util/hasAccessibleChild';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
|
||||
|
||||
describe('hasAccessibleChild', () => {
|
||||
describe('has no children and does not set dangerouslySetInnerHTML', () => {
|
||||
it('returns false', () => {
|
||||
expect(hasAccessibleChild(JSXElementMock('div', []))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('has no children and sets dangerouslySetInnerHTML', () => {
|
||||
it('Returns true', () => {
|
||||
const prop = JSXAttributeMock('dangerouslySetInnerHTML', true);
|
||||
const element = JSXElementMock('div', [prop], []);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('has children', () => {
|
||||
it('Returns true for a Literal child', () => {
|
||||
const child = {
|
||||
type: 'Literal',
|
||||
value: 'foo',
|
||||
};
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
|
||||
it('Returns true for visible child JSXElement', () => {
|
||||
const child = JSXElementMock('div', []);
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
|
||||
it('Returns true for JSXText Element', () => {
|
||||
const child = {
|
||||
type: 'JSXText',
|
||||
value: 'foo',
|
||||
};
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
|
||||
it('Returns false for hidden child JSXElement', () => {
|
||||
const ariaHiddenAttr = JSXAttributeMock('aria-hidden', true);
|
||||
const child = JSXElementMock('div', [ariaHiddenAttr]);
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(false);
|
||||
});
|
||||
|
||||
it('Returns true for defined JSXExpressionContainer', () => {
|
||||
const expression = {
|
||||
type: 'Identifier',
|
||||
name: 'foo',
|
||||
};
|
||||
const child = JSXExpressionContainerMock(expression);
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
|
||||
it('Returns false for undefined JSXExpressionContainer', () => {
|
||||
const expression = {
|
||||
type: 'Identifier',
|
||||
name: 'undefined',
|
||||
};
|
||||
const child = JSXExpressionContainerMock(expression);
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(false);
|
||||
});
|
||||
|
||||
it('Returns false for unknown child type', () => {
|
||||
const child = {
|
||||
type: 'Unknown',
|
||||
};
|
||||
const element = JSXElementMock('div', [], [child]);
|
||||
expect(hasAccessibleChild(element)).toBe(false);
|
||||
});
|
||||
|
||||
it('Returns true with children passed as a prop', () => {
|
||||
const children = JSXAttributeMock('children', true);
|
||||
const element = JSXElementMock('div', [children], []);
|
||||
expect(hasAccessibleChild(element)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
35
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/input-test.js
generated
vendored
Normal file
35
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/input-test.js
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
|
||||
import getImplicitRoleForInput from '../../../../src/util/implicitRoles/input';
|
||||
|
||||
describe('isAbstractRole', () => {
|
||||
it('works for buttons', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'button')])).toBe('button');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'image')])).toBe('button');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'reset')])).toBe('button');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'submit')])).toBe('button');
|
||||
});
|
||||
it('works for checkboxes', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'checkbox')])).toBe('checkbox');
|
||||
});
|
||||
it('works for radios', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'radio')])).toBe('radio');
|
||||
});
|
||||
it('works for ranges', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'range')])).toBe('slider');
|
||||
});
|
||||
it('works for textboxes', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'email')])).toBe('textbox');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'password')])).toBe('textbox');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'search')])).toBe('textbox');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'tel')])).toBe('textbox');
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', 'url')])).toBe('textbox');
|
||||
});
|
||||
it('works for the default case', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', '')])).toBe('textbox');
|
||||
});
|
||||
it('works for the true case', () => {
|
||||
expect(getImplicitRoleForInput([JSXAttributeMock('type', true)])).toBe('textbox');
|
||||
});
|
||||
});
|
13
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/menu-test.js
generated
vendored
Normal file
13
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/menu-test.js
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
|
||||
import getImplicitRoleForMenu from '../../../../src/util/implicitRoles/menu';
|
||||
|
||||
describe('isAbstractRole', () => {
|
||||
it('works for toolbars', () => {
|
||||
expect(getImplicitRoleForMenu([JSXAttributeMock('type', 'toolbar')])).toBe('toolbar');
|
||||
});
|
||||
it('works for non-toolbars', () => {
|
||||
expect(getImplicitRoleForMenu([JSXAttributeMock('type', '')])).toBe('');
|
||||
});
|
||||
});
|
22
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/menuitem-test.js
generated
vendored
Normal file
22
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/implicitRoles/menuitem-test.js
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
|
||||
import getImplicitRoleForMenuitem from '../../../../src/util/implicitRoles/menuitem';
|
||||
|
||||
describe('isAbstractRole', () => {
|
||||
it('works for menu items', () => {
|
||||
expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'command')])).toBe('menuitem');
|
||||
});
|
||||
it('works for menu item checkboxes', () => {
|
||||
expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'checkbox')])).toBe('menuitemcheckbox');
|
||||
});
|
||||
it('works for menu item radios', () => {
|
||||
expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'radio')])).toBe('menuitemradio');
|
||||
});
|
||||
it('works for non-toolbars', () => {
|
||||
expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', '')])).toBe('');
|
||||
});
|
||||
it('works for the true case', () => {
|
||||
expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', true)])).toBe('');
|
||||
});
|
||||
});
|
40
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isAbstractRole-test.js
generated
vendored
Normal file
40
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isAbstractRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isAbstractRole from '../../../src/util/isAbstractRole';
|
||||
import {
|
||||
genElementSymbol,
|
||||
genAbstractRoleElements,
|
||||
genNonAbstractRoleElements,
|
||||
} from '../../../__mocks__/genInteractives';
|
||||
|
||||
describe('isAbstractRole', () => {
|
||||
describe('JSX Components (no tagName)', () => {
|
||||
it('should NOT identify them as abstract role elements', () => {
|
||||
expect(isAbstractRole(undefined, []))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with an abstract role', () => {
|
||||
genAbstractRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should identify \`${genElementSymbol(openingElement)}\` as an abstract role element`, () => {
|
||||
expect(isAbstractRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('elements with a non-abstract role', () => {
|
||||
genNonAbstractRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an abstract role element`, () => {
|
||||
expect(isAbstractRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
27
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isDOMElement-test.js
generated
vendored
Normal file
27
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isDOMElement-test.js
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* eslint-env mocha */
|
||||
import { dom } from 'aria-query';
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isDOMElement from '../../../src/util/isDOMElement';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
|
||||
const domElements = [...dom.keys()];
|
||||
|
||||
describe('isDOMElement', () => {
|
||||
describe('DOM elements', () => {
|
||||
domElements.forEach((el) => {
|
||||
it(`should identify ${el} as a DOM element`, () => {
|
||||
const element = JSXElementMock(el);
|
||||
expect(isDOMElement(elementType(element.openingElement)))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Custom Element', () => {
|
||||
it('should not identify a custom element', () => {
|
||||
const element = JSXElementMock('CustomElement');
|
||||
expect(isDOMElement(element))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
82
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isDisabledElement-test.js
generated
vendored
Normal file
82
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isDisabledElement-test.js
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import isDisabledElement from '../../../src/util/isDisabledElement';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
|
||||
describe('isDisabledElement', () => {
|
||||
describe('HTML5', () => {
|
||||
describe('disabled', () => {
|
||||
it('should identify HTML5 disabled elements', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('disabled', 'disabled'),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
describe('not disabled', () => {
|
||||
it('should identify HTML5 disabled elements with null as the value', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('disabled', null),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
it('should not identify HTML5 disabled elements with undefined as the value', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('disabled', undefined),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('ARIA', () => {
|
||||
describe('disabled', () => {
|
||||
it('should not identify ARIA disabled elements', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', 'true'),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
it('should not identify ARIA disabled elements', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', true),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
describe('not disabled', () => {
|
||||
it('should not identify ARIA disabled elements', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', 'false'),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should not identify ARIA disabled elements', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', false),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should not identify ARIA disabled elements with null as the value', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', null),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
it('should not identify ARIA disabled elements with undefined as the value', () => {
|
||||
const attributes = [
|
||||
JSXAttributeMock('aria-disabled', undefined),
|
||||
];
|
||||
expect(isDisabledElement(attributes))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
77
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isInteractiveElement-test.js
generated
vendored
Normal file
77
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isInteractiveElement-test.js
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isInteractiveElement from '../../../src/util/isInteractiveElement';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
import {
|
||||
genElementSymbol,
|
||||
genIndeterminantInteractiveElements,
|
||||
genInteractiveElements,
|
||||
genInteractiveRoleElements,
|
||||
genNonInteractiveElements,
|
||||
genNonInteractiveRoleElements,
|
||||
} from '../../../__mocks__/genInteractives';
|
||||
|
||||
describe('isInteractiveElement', () => {
|
||||
describe('JSX Components (no tagName)', () => {
|
||||
it('should identify them as interactive elements', () => {
|
||||
expect(isInteractiveElement(undefined, []))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('interactive elements', () => {
|
||||
genInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
|
||||
expect(isInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('interactive role elements', () => {
|
||||
genInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
|
||||
expect(isInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('non-interactive elements', () => {
|
||||
genNonInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
|
||||
expect(isInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('non-interactive role elements', () => {
|
||||
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
|
||||
expect(isInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('indeterminate elements', () => {
|
||||
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${openingElement.name.name}\` as an interactive element`, () => {
|
||||
expect(isInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('JSX elements', () => {
|
||||
it('is not interactive', () => {
|
||||
expect(isInteractiveElement('CustomComponent', JSXElementMock())).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isInteractiveRole-test.js
generated
vendored
Normal file
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isInteractiveRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isInteractiveRole from '../../../src/util/isInteractiveRole';
|
||||
import {
|
||||
genElementSymbol,
|
||||
genInteractiveRoleElements,
|
||||
genNonInteractiveRoleElements,
|
||||
} from '../../../__mocks__/genInteractives';
|
||||
|
||||
describe('isInteractiveRole', () => {
|
||||
describe('JSX Components (no tagName)', () => {
|
||||
it('should identify them as interactive role elements', () => {
|
||||
expect(isInteractiveRole(undefined, []))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a non-interactive role', () => {
|
||||
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should not identify \`${genElementSymbol(openingElement)}\` as an interactive role element`, () => {
|
||||
expect(isInteractiveRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('elements without a role', () => {
|
||||
it('should not identify them as interactive role elements', () => {
|
||||
expect(isInteractiveRole('div', [])).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with an interactive role', () => {
|
||||
genInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should identify \`${genElementSymbol(openingElement)}\` as an interactive role element`, () => {
|
||||
expect(isInteractiveRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonInteractiveElement-test.js
generated
vendored
Normal file
71
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonInteractiveElement-test.js
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isNonInteractiveElement from '../../../src/util/isNonInteractiveElement';
|
||||
import {
|
||||
genElementSymbol,
|
||||
genIndeterminantInteractiveElements,
|
||||
genInteractiveElements,
|
||||
genInteractiveRoleElements,
|
||||
genNonInteractiveElements,
|
||||
genNonInteractiveRoleElements,
|
||||
} from '../../../__mocks__/genInteractives';
|
||||
|
||||
describe('isNonInteractiveElement', () => {
|
||||
describe('JSX Components (no tagName)', () => {
|
||||
it('should identify them as interactive elements', () => {
|
||||
expect(isNonInteractiveElement(undefined, []))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('non-interactive elements', () => {
|
||||
genNonInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
|
||||
expect(isNonInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('non-interactive role elements', () => {
|
||||
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
|
||||
expect(isNonInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('interactive elements', () => {
|
||||
genInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
|
||||
expect(isNonInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('interactive role elements', () => {
|
||||
genInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
|
||||
expect(isNonInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('indeterminate elements', () => {
|
||||
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
|
||||
it(`should NOT identify \`${openingElement.name.name}\` as a non-interactive element`, () => {
|
||||
expect(isNonInteractiveElement(
|
||||
elementType(openingElement),
|
||||
openingElement.attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonInteractiveRole-test.js
generated
vendored
Normal file
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonInteractiveRole-test.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import { elementType } from 'jsx-ast-utils';
|
||||
import isNonInteractiveRole from '../../../src/util/isNonInteractiveRole';
|
||||
import {
|
||||
genElementSymbol,
|
||||
genInteractiveRoleElements,
|
||||
genNonInteractiveRoleElements,
|
||||
} from '../../../__mocks__/genInteractives';
|
||||
|
||||
describe('isNonInteractiveRole', () => {
|
||||
describe('JSX Components (no tagName)', () => {
|
||||
it('should identify them as interactive role elements', () => {
|
||||
expect(isNonInteractiveRole(undefined, []))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a non-interactive role', () => {
|
||||
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should identify \`${genElementSymbol(openingElement)}\` as non-interactive role element`, () => {
|
||||
expect(isNonInteractiveRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('elements without a role', () => {
|
||||
it('should not identify them as non-interactive role elements', () => {
|
||||
expect(isNonInteractiveRole('div', [])).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with an interactive role', () => {
|
||||
genInteractiveRoleElements().forEach(({ openingElement }) => {
|
||||
const { attributes } = openingElement;
|
||||
it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive role element`, () => {
|
||||
expect(isNonInteractiveRole(
|
||||
elementType(openingElement),
|
||||
attributes,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonLiteralProperty-test.js
generated
vendored
Normal file
45
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isNonLiteralProperty-test.js
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import isNonLiteralProperty from '../../../src/util/isNonLiteralProperty';
|
||||
import IdentifierMock from '../../../__mocks__/IdentifierMock';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
import JSXSpreadAttributeMock from '../../../__mocks__/JSXSpreadAttributeMock';
|
||||
import JSXTextMock from '../../../__mocks__/JSXTextMock';
|
||||
import LiteralMock from '../../../__mocks__/LiteralMock';
|
||||
|
||||
const theProp = 'theProp';
|
||||
|
||||
const spread = JSXSpreadAttributeMock('theSpread');
|
||||
|
||||
describe('isNonLiteralProperty', () => {
|
||||
describe('elements without the property', () => {
|
||||
it('should not identify them as non-literal role elements', () => {
|
||||
expect(isNonLiteralProperty([], theProp)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a literal property', () => {
|
||||
it('should not identify them as non-literal role elements without spread operator', () => {
|
||||
expect(isNonLiteralProperty([JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp)).toBe(false);
|
||||
});
|
||||
it('should not identify them as non-literal role elements with spread operator', () => {
|
||||
expect(isNonLiteralProperty([spread, JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a JSXText property', () => {
|
||||
it('should not identify them as non-literal role elements', () => {
|
||||
expect(isNonLiteralProperty([JSXAttributeMock(theProp, JSXTextMock('theRole'))], theProp)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a property of undefined', () => {
|
||||
it('should not identify them as non-literal role elements', () => {
|
||||
const undefinedExpression = IdentifierMock('undefined');
|
||||
expect(isNonLiteralProperty([JSXAttributeMock(theProp, undefinedExpression)], theProp)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('elements with a expression property', () => {
|
||||
it('should identify them as non-literal role elements', () => {
|
||||
const identifierExpression = IdentifierMock('theIdentifier');
|
||||
expect(isNonLiteralProperty([JSXAttributeMock(theProp, identifierExpression)], theProp)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
48
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isSemanticRoleElement-test.js
generated
vendored
Normal file
48
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/isSemanticRoleElement-test.js
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import isSemanticRoleElement from '../../../src/util/isSemanticRoleElement';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
|
||||
describe('isSemanticRoleElement', () => {
|
||||
it('should identify semantic role elements', () => {
|
||||
expect(isSemanticRoleElement('input', [
|
||||
JSXAttributeMock('type', 'checkbox'),
|
||||
JSXAttributeMock('role', 'switch'),
|
||||
])).toBe(true);
|
||||
});
|
||||
it('should reject non-semantic role elements', () => {
|
||||
expect(isSemanticRoleElement('input', [
|
||||
JSXAttributeMock('type', 'radio'),
|
||||
JSXAttributeMock('role', 'switch'),
|
||||
])).toBe(false);
|
||||
expect(isSemanticRoleElement('input', [
|
||||
JSXAttributeMock('type', 'text'),
|
||||
JSXAttributeMock('role', 'combobox'),
|
||||
])).toBe(false);
|
||||
expect(isSemanticRoleElement('button', [
|
||||
JSXAttributeMock('role', 'switch'),
|
||||
JSXAttributeMock('aria-pressed', 'true'),
|
||||
])).toBe(false);
|
||||
expect(isSemanticRoleElement('input', [
|
||||
JSXAttributeMock('role', 'switch'),
|
||||
])).toBe(false);
|
||||
});
|
||||
it('should not throw on JSXSpreadAttribute', () => {
|
||||
expect(() => {
|
||||
isSemanticRoleElement('input', [
|
||||
JSXAttributeMock('type', 'checkbox'),
|
||||
JSXAttributeMock('role', 'checkbox'),
|
||||
JSXAttributeMock('aria-checked', 'false'),
|
||||
JSXAttributeMock('aria-labelledby', 'foo'),
|
||||
JSXAttributeMock('tabindex', '0'),
|
||||
{
|
||||
type: 'JSXSpreadAttribute',
|
||||
argument: {
|
||||
type: 'Identifier',
|
||||
name: 'props',
|
||||
},
|
||||
},
|
||||
]);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
107
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/mayContainChildComponent-test.js
generated
vendored
Normal file
107
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/mayContainChildComponent-test.js
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* eslint-env jest */
|
||||
import mayContainChildComponent from '../../../src/util/mayContainChildComponent';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
|
||||
|
||||
describe('mayContainChildComponent', () => {
|
||||
describe('no FancyComponent', () => {
|
||||
it('should return false', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
]),
|
||||
]),
|
||||
'FancyComponent',
|
||||
5,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('contains an indicated component', () => {
|
||||
it('should return true', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('input'),
|
||||
]),
|
||||
'input',
|
||||
)).toBe(true);
|
||||
});
|
||||
it('should return true', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('FancyComponent'),
|
||||
]),
|
||||
'FancyComponent',
|
||||
)).toBe(true);
|
||||
});
|
||||
it('FancyComponent is outside of default depth, should return false', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('FancyComponent'),
|
||||
]),
|
||||
]),
|
||||
'FancyComponent',
|
||||
)).toBe(false);
|
||||
});
|
||||
it('FancyComponent is inside of custom depth, should return true', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('FancyComponent'),
|
||||
]),
|
||||
]),
|
||||
'FancyComponent',
|
||||
2,
|
||||
)).toBe(true);
|
||||
});
|
||||
it('deep nesting, should return true', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('FancyComponent'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
]),
|
||||
]),
|
||||
'FancyComponent',
|
||||
6,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('Intederminate situations', () => {
|
||||
describe('expression container children', () => {
|
||||
it('should return true', () => {
|
||||
expect(mayContainChildComponent(
|
||||
JSXElementMock('div', [], [
|
||||
JSXExpressionContainerMock('mysteryBox'),
|
||||
]),
|
||||
'FancyComponent',
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
170
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/mayHaveAccessibleLabel-test.js
generated
vendored
Normal file
170
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/mayHaveAccessibleLabel-test.js
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
/* eslint-env jest */
|
||||
import mayHaveAccessibleLabel from '../../../src/util/mayHaveAccessibleLabel';
|
||||
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
|
||||
import JSXElementMock from '../../../__mocks__/JSXElementMock';
|
||||
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
|
||||
import JSXSpreadAttributeMock from '../../../__mocks__/JSXSpreadAttributeMock';
|
||||
import JSXTextMock from '../../../__mocks__/JSXTextMock';
|
||||
import LiteralMock from '../../../__mocks__/LiteralMock';
|
||||
|
||||
describe('mayHaveAccessibleLabel', () => {
|
||||
describe('no label', () => {
|
||||
it('should return false', () => {
|
||||
expect(mayHaveAccessibleLabel(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
]),
|
||||
]),
|
||||
5,
|
||||
)).toBe(false);
|
||||
});
|
||||
});
|
||||
describe('label via attributes', () => {
|
||||
it('aria-label, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('aria-label', 'A delicate label'),
|
||||
], []))).toBe(true);
|
||||
});
|
||||
it('aria-label without content, should return false', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('aria-label', ''),
|
||||
], []))).toBe(false);
|
||||
});
|
||||
it('aria-labelledby, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('aria-labelledby', 'elementId'),
|
||||
], []))).toBe(true);
|
||||
});
|
||||
it('aria-labelledby without content, should return false', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('aria-labelledby', ''),
|
||||
], []))).toBe(false);
|
||||
});
|
||||
it('aria-labelledby with an expression container, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('aria-labelledby', 'elementId', true),
|
||||
], []))).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('label via custom label attribute', () => {
|
||||
let customLabelProp;
|
||||
beforeEach(() => {
|
||||
customLabelProp = 'cowbell';
|
||||
});
|
||||
it('aria-label, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(
|
||||
JSXElementMock('div', [
|
||||
JSXAttributeMock(customLabelProp, 'A delicate label'),
|
||||
], []),
|
||||
1,
|
||||
[customLabelProp],
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('text label', () => {
|
||||
it('Literal text, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
LiteralMock('A fancy label'),
|
||||
]))).toBe(true);
|
||||
});
|
||||
it('JSXText, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXTextMock('A fancy label'),
|
||||
]))).toBe(true);
|
||||
});
|
||||
it('label is outside of default depth, should return false', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXTextMock('A fancy label'),
|
||||
]),
|
||||
]))).toBe(false);
|
||||
});
|
||||
it('label is inside of custom depth, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXTextMock('A fancy label'),
|
||||
]),
|
||||
]),
|
||||
2,
|
||||
)).toBe(true);
|
||||
});
|
||||
it('deep nesting, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('div', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], [
|
||||
JSXElementMock('span', [], [
|
||||
JSXTextMock('A fancy label'),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
JSXElementMock('span', [], []),
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
]),
|
||||
]),
|
||||
6,
|
||||
)).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('image content', () => {
|
||||
it('without alt, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
]),
|
||||
]))).toBe(false);
|
||||
});
|
||||
it('with alt, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
JSXAttributeMock('alt', 'A sensible label'),
|
||||
]),
|
||||
]))).toBe(true);
|
||||
});
|
||||
it('with aria-label, should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXElementMock('img', [
|
||||
JSXAttributeMock('src', 'some/path'),
|
||||
JSXAttributeMock('aria-label', 'A sensible label'),
|
||||
]),
|
||||
]))).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('Intederminate situations', () => {
|
||||
describe('expression container children', () => {
|
||||
it('should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
|
||||
JSXExpressionContainerMock('mysteryBox'),
|
||||
]))).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('spread operator in attributes', () => {
|
||||
it('should return true', () => {
|
||||
expect(mayHaveAccessibleLabel(JSXElementMock('div', [
|
||||
JSXAttributeMock('style', 'some-junk'),
|
||||
JSXSpreadAttributeMock('props'),
|
||||
], []))).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
47
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/parserOptionsMapper-test.js
generated
vendored
Normal file
47
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/parserOptionsMapper-test.js
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* eslint-env mocha */
|
||||
import expect from 'expect';
|
||||
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
|
||||
|
||||
describe('parserOptionsMapper', () => {
|
||||
it('should return an test case object', () => {
|
||||
const testCase = {
|
||||
code: '<div />',
|
||||
errors: [],
|
||||
options: {},
|
||||
};
|
||||
expect(parserOptionsMapper(testCase)).toEqual({
|
||||
code: '<div />',
|
||||
errors: [],
|
||||
options: {},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
it('should allow for overriding parserOptions', () => {
|
||||
const testCase = {
|
||||
code: '<div />',
|
||||
errors: [],
|
||||
options: {},
|
||||
parserOptions: {
|
||||
ecmaVersion: 5,
|
||||
},
|
||||
};
|
||||
expect(parserOptionsMapper(testCase)).toEqual({
|
||||
code: '<div />',
|
||||
errors: [],
|
||||
options: {},
|
||||
parserOptions: {
|
||||
ecmaVersion: 5,
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
30
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/schemas-test.js
generated
vendored
Normal file
30
web/node_modules/eslint-plugin-jsx-a11y/__tests__/src/util/schemas-test.js
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* eslint-env jest */
|
||||
import assert from 'assert';
|
||||
import { generateObjSchema, arraySchema, enumArraySchema } from '../../../src/util/schemas';
|
||||
|
||||
describe('schemas', () => {
|
||||
it('should generate an object schema with correct properties', () => {
|
||||
const schema = generateObjSchema({
|
||||
foo: 'bar',
|
||||
baz: arraySchema,
|
||||
});
|
||||
const properties = schema.properties || {};
|
||||
|
||||
assert.deepEqual(properties.foo, 'bar');
|
||||
assert.deepEqual(properties.baz.type, 'array');
|
||||
});
|
||||
describe('enumArraySchema', () => {
|
||||
it('works with no arguments', () => {
|
||||
assert.deepEqual(enumArraySchema(), {
|
||||
additionalItems: false,
|
||||
items: {
|
||||
enum: [],
|
||||
type: 'string',
|
||||
},
|
||||
minItems: 0,
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue