Socket
Socket
Sign inDemoInstall

eslint-plugin-jsx-a11y

Package Overview
Dependencies
3
Maintainers
2
Versions
81
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.4.0 to 1.4.1

.eslintrc

8

CHANGELOG.md

@@ -0,5 +1,9 @@

1.4.1 / 2016-06-10
==================
- [fix] Handle spread props in `aria-unsupported-elements` and `role-supports-aria-props` when reporting.
1.4.0 / 2016-06-10
==================
- [dependency] Integrate `jsx-ast-utils` (extracted JSX core module)
1. [jsx-ast-utils](https://github.com/evcohen/jsx-ast-utils)
- [dependency] Integrate [jsx-ast-utils](https://github.com/evcohen/jsx-ast-utils)
- [fix] Better error reporting for aria-unsupported-elements indicating which prop to remove.

@@ -6,0 +10,0 @@

'use strict';
/* eslint-disable global-require */
module.exports = {

@@ -4,0 +6,0 @@ rules: {

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce all aria-* properties are valid.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _ARIA = require('../util/attributes/ARIA');

@@ -21,2 +13,11 @@

/**
* @fileoverview Enforce all aria-* properties are valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(name) {

@@ -23,0 +24,0 @@ var dictionary = Object.keys(_ARIA2.default).map(function (aria) {

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce ARIA state and property values are valid.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _ARIA = require('../util/attributes/ARIA');

@@ -19,2 +11,11 @@

/**
* @fileoverview Enforce ARIA state and property values are valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(name, type, permittedValues) {

@@ -44,3 +45,3 @@ switch (type) {

case 'tristate':
return typeof value === 'boolean' || value == 'mixed';
return typeof value === 'boolean' || value === 'mixed';
case 'integer':

@@ -47,0 +48,0 @@ case 'number':

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce aria role attribute is valid.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _role = require('../util/attributes/role');

@@ -19,2 +11,11 @@

/**
* @fileoverview Enforce aria role attribute is valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Elements with ARIA roles must use a valid, non-abstract ARIA role.';

@@ -33,3 +34,4 @@

// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the value isn't in the form of a literal.
// If value is null, then getLiteralAttributeValue is telling us that the
// value isn't in the form of a literal.
if (value === undefined || value === null) {

@@ -43,4 +45,4 @@ return;

});
var isValid = normalizedValues.every(function (value) {
return validRoles.indexOf(value) > -1;
var isValid = normalizedValues.every(function (val) {
return validRoles.indexOf(val) > -1;
});

@@ -47,0 +49,0 @@

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce that elements that do not support ARIA roles, states and properties do not have those attributes.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _DOM = require('../util/attributes/DOM');

@@ -25,4 +17,12 @@

return 'This element does not support ARIA roles, states and properties. Try removing the prop \'' + invalidProp + '\'.';
};
}; /**
* @fileoverview Enforce that elements that do not support ARIA roles,
* states and properties do not have those attributes.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
module.exports = function (context) {

@@ -43,2 +43,6 @@ return {

node.attributes.forEach(function (prop) {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
if (invalidAttributes.indexOf(prop.name.name.toUpperCase()) > -1) {

@@ -45,0 +49,0 @@ context.report({

@@ -1,7 +0,10 @@

/**
* @fileoverview Enforce links may not point to just #.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Links must not point to "#". ' + 'Use a more descriptive href or use a button instead.'; /**
* @fileoverview Enforce links may not point to just #.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -11,6 +14,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Links must not point to "#". Use a more descriptive href or use a button instead.';
module.exports = function (context) {

@@ -41,10 +40,10 @@ return {

module.exports.schema = [{
'oneOf': [{ 'type': 'string' }, {
'type': 'array',
'items': {
'type': 'string'
oneOf: [{ type: 'string' }, {
type: 'array',
items: {
type: 'string'
},
'minItems': 1,
'uniqueItems': true
minItems: 1,
uniqueItems: true
}]
}];

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce img tag uses alt attribute.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _jsxAstUtils = require('jsx-ast-utils');

@@ -58,13 +50,20 @@

};
};
}; /**
* @fileoverview Enforce img tag uses alt attribute.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
module.exports.schema = [{
'oneOf': [{ 'type': 'string' }, {
'type': 'array',
'items': {
'type': 'string'
oneOf: [{ type: 'string' }, {
type: 'array',
items: {
type: 'string'
},
'minItems': 1,
'uniqueItems': true
minItems: 1,
uniqueItems: true
}]
}];

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce img alt attribute does not have the word image, picture, or photo.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _jsxAstUtils = require('jsx-ast-utils');

@@ -19,5 +11,14 @@

/**
* @fileoverview Enforce img alt attribute does not have the word image, picture, or photo.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var REDUNDANT_WORDS = ['image', 'photo', 'picture'];
var errorMessage = 'Redundant alt attribute. Screen-readers already announce `img` tags as an image. ' + 'You don\'t need to use the words `image`, `photo,` or `picture` in the alt prop.';
var errorMessage = 'Redundant alt attribute. Screen-readers already announce ' + '`img` tags as an image. You don\'t need to use the words `image`, ' + '`photo,` or `picture` in the alt prop.';

@@ -24,0 +25,0 @@ var validTypes = ['LITERAL', 'TEMPLATELITERAL'];

@@ -1,7 +0,10 @@

/**
* @fileoverview Enforce label tags have htmlFor attribute.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Form controls using a label to identify them must be ' + 'programmatically associated with the control using htmlFor'; /**
* @fileoverview Enforce label tags have htmlFor attribute.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -11,6 +14,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Form controls using a label to identify them must be ' + 'programmatically associated with the control using htmlFor';
module.exports = function (context) {

@@ -42,10 +41,10 @@ return {

module.exports.schema = [{
'oneOf': [{ 'type': 'string' }, {
'type': 'array',
'items': {
'type': 'string'
oneOf: [{ type: 'string' }, {
type: 'array',
items: {
type: 'string'
},
'minItems': 1,
'uniqueItems': true
minItems: 1,
uniqueItems: true
}]
}];

@@ -1,8 +0,11 @@

/**
* @fileoverview Enforce onmouseover/onmouseout are
* accompanied by onfocus/onblur.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var mouseOverErrorMessage = 'onMouseOver must be accompanied by onFocus for accessibility.'; /**
* @fileoverview Enforce onmouseover/onmouseout are
* accompanied by onfocus/onblur.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -12,5 +15,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var mouseOverErrorMessage = 'onMouseOver must be accompanied by onFocus for accessibility.';
var mouseOutErrorMessage = 'onMouseOut must be accompanied by onBlur for accessibility.';

@@ -17,0 +17,0 @@

@@ -1,7 +0,10 @@

/**
* @fileoverview Enforce no accesskey attribute on element.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'No access key attribute allowed. Inconsistencies ' + 'between keyboard shortcuts and keyboard comments used by screenreader ' + 'and keyboard only users create a11y complications.'; /**
* @fileoverview Enforce no accesskey attribute on element.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -11,6 +14,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'No access key attribute allowed. Inconsistencies ' + 'between keyboard shortcuts and keyboard comments used by screenreader ' + 'and keyboard only users create a11y complications.';
module.exports = function (context) {

@@ -17,0 +16,0 @@ return {

@@ -1,7 +0,10 @@

/**
* @fileoverview Enforce usage of onBlur over onChange for accessibility.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'onBlur must be used instead of onchange, ' + 'unless absolutely necessary and it causes no negative consequences ' + 'for keyboard only or screen reader users.'; /**
* @fileoverview Enforce usage of onBlur over onChange for accessibility.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -11,6 +14,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'onBlur must be used instead of onchange, ' + 'unless absolutely necessary and it causes no negative consequences ' + 'for keyboard only or screen reader users.';
module.exports = function (context) {

@@ -17,0 +16,0 @@ return {

@@ -1,5 +0,1 @@

/**
* @fileoverview Enforce that elements with onClick handlers must be focusable.
* @author Ethan Cohen
*/
'use strict';

@@ -27,2 +23,7 @@

/**
* @fileoverview Enforce that elements with onClick handlers must be focusable.
* @author Ethan Cohen
*/
var errorMessage = 'Elements with onClick handlers must be focusable. ' + 'Either set the tabIndex property to a valid value (usually 0), or use ' + 'an element type which is inherently focusable such as `button`.';

@@ -29,0 +30,0 @@

@@ -1,6 +0,1 @@

/**
* @fileoverview Enforce non-interactive elements with
* click handlers use role attribute.
* @author Ethan Cohen
*/
'use strict';

@@ -24,3 +19,7 @@

var errorMessage = 'Visible, non-interactive elements with click handlers must ' + 'have role attribute.';
var errorMessage = 'Visible, non-interactive elements with click handlers must ' + 'have role attribute.'; /**
* @fileoverview Enforce non-interactive elements with
* click handlers use role attribute.
* @author Ethan Cohen
*/

@@ -27,0 +26,0 @@ module.exports = function (context) {

@@ -1,11 +0,3 @@

/**
* @fileoverview Enforce that elements with ARIA roles must have all required attributes for that role.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _role = require('../util/attributes/role');

@@ -19,2 +11,12 @@

/**
* @fileoverview Enforce that elements with ARIA roles must
* have all required attributes for that role.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(role, requiredProps) {

@@ -35,3 +37,4 @@ return 'Elements with the ARIA role "' + role + '" must have the following ' + ('attributes defined: ' + String(requiredProps).toLowerCase());

// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the value isn't in the form of a literal.
// If value is null, then getLiteralAttributeValue is telling us
// that the value isn't in the form of a literal.
if (value === undefined || value === null) {

@@ -42,4 +45,4 @@ return;

var normalizedValues = String(value).toUpperCase().split(' ');
var validRoles = normalizedValues.filter(function (value) {
return Object.keys(_role2.default).indexOf(value) > -1;
var validRoles = normalizedValues.filter(function (val) {
return Object.keys(_role2.default).indexOf(val) > -1;
});

@@ -46,0 +49,0 @@

@@ -1,12 +0,3 @@

/**
* @fileoverview Enforce that elements with explicit or implicit roles defined contain only
* `aria-*` properties supported by that `role`.
* @author Ethan Cohen
*/
'use strict';
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _role = require('../util/attributes/role');

@@ -28,2 +19,12 @@

/**
* @fileoverview Enforce that elements with explicit or implicit roles defined contain only
* `aria-*` properties supported by that `role`.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(attr, role, tag, isImplicit) {

@@ -60,2 +61,6 @@ if (isImplicit) {

node.attributes.forEach(function (prop) {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
if (invalidAriaPropsForRole.indexOf(prop.name.name.toUpperCase()) > -1) {

@@ -62,0 +67,0 @@ context.report({

@@ -1,7 +0,10 @@

/**
* @fileoverview Enforce tabIndex value is not greater than zero.
* @author Ethan Cohen
*/
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Avoid positive integer values for tabIndex.'; /**
* @fileoverview Enforce tabIndex value is not greater than zero.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------

@@ -11,6 +14,2 @@ // Rule Definition

var _jsxAstUtils = require('jsx-ast-utils');
var errorMessage = 'Avoid positive integer values for tabIndex.';
module.exports = function (context) {

@@ -17,0 +16,0 @@ return {

@@ -26,3 +26,6 @@ 'use strict';

var distances = dictionary.reduce(function (suggestions, dictionaryWord) {
suggestions[dictionaryWord] = (0, _damerauLevenshtein2.default)(word.toUpperCase(), dictionaryWord.toUpperCase()).steps;
var distance = (0, _damerauLevenshtein2.default)(word.toUpperCase(), dictionaryWord.toUpperCase());
var steps = distance.steps;
suggestions[dictionaryWord] = steps; // eslint-disable-line
return suggestions;

@@ -29,0 +32,0 @@ }, {});

@@ -20,3 +20,3 @@ 'use strict';

if (hidden && hidden.toUpperCase() == 'HIDDEN') {
if (hidden && hidden.toUpperCase() === 'HIDDEN') {
return true;

@@ -23,0 +23,0 @@ }

{
"name": "eslint-plugin-jsx-a11y",
"version": "1.4.0",
"version": "1.4.1",
"description": "A static analysis linter of jsx and their accessibility with screen readers.",

@@ -23,3 +23,3 @@ "keywords": [

"coveralls": "cat ./reports/coverage/lcov.info | coveralls",
"lint": "eslint --config .eslintrc.js .",
"lint": "eslint --config .eslintrc src tests",
"pretest": "npm run lint",

@@ -35,2 +35,4 @@ "test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/**/*.js -- --compilers js:babel-core/register --reporter dot"

"eslint": "^2.2.0",
"eslint-config-airbnb-base": "^3.0.1",
"eslint-plugin-import": "^1.8.1",
"istanbul": "^1.0.0-alpha.2",

@@ -37,0 +39,0 @@ "mocha": "^2.4.5",

@@ -1,2 +0,2 @@

'use strict';
/* eslint-disable global-require */

@@ -20,3 +20,3 @@ module.exports = {

'role-supports-aria-props': require('./rules/role-supports-aria-props'),
'tabindex-no-positive': require('./rules/tabindex-no-positive')
'tabindex-no-positive': require('./rules/tabindex-no-positive'),
},

@@ -27,4 +27,4 @@ configs: {

ecmaFeatures: {
jsx: true
}
jsx: true,
},
},

@@ -47,6 +47,6 @@ rules: {

'jsx-a11y/role-supports-aria-props': 2,
'jsx-a11y/tabindex-no-positive': 2
}
}
}
'jsx-a11y/tabindex-no-positive': 2,
},
},
},
};

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -42,10 +41,10 @@ // ----------------------------------------------------------------------------

node: attribute,
message: errorMessage(name)
message: errorMessage(name),
});
}
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -22,3 +21,4 @@ // ----------------------------------------------------------------------------

case 'tokenlist':
return `The value for ${name} must be a list of one or more tokens from the following: ${permittedValues}.`;
return `The value for ${name} must be a list of one or more \
tokens from the following: ${permittedValues}.`;
case 'boolean':

@@ -40,3 +40,3 @@ case 'string':

case 'tristate':
return typeof value === 'boolean' || value == 'mixed';
return typeof value === 'boolean' || value === 'mixed';
case 'integer':

@@ -49,3 +49,4 @@ case 'number':

case 'tokenlist':
return typeof value === 'string' && value.split(' ').every(token => permittedValues.indexOf(token.toLowerCase()) > -1);
return typeof value === 'string' &&
value.split(' ').every(token => permittedValues.indexOf(token.toLowerCase()) > -1);
default:

@@ -79,3 +80,4 @@ return false;

const isValid = validityCheck(value, permittedType, permittedValues) || (allowUndefined && value === undefined);
const isValid = validityCheck(value, permittedType, permittedValues) ||
(allowUndefined && value === undefined);

@@ -88,9 +90,9 @@ if (isValid) {

node: attribute,
message: errorMessage(name, permittedType, permittedValues)
message: errorMessage(name, permittedType, permittedValues),
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -27,3 +26,4 @@ // ----------------------------------------------------------------------------

// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the value isn't in the form of a literal.
// If value is null, then getLiteralAttributeValue is telling us that the
// value isn't in the form of a literal.
if (value === undefined || value === null) {

@@ -35,3 +35,3 @@ return;

const validRoles = Object.keys(roles).filter(role => roles[role].abstract === false);
const isValid = normalizedValues.every(value => validRoles.indexOf(value) > -1);
const isValid = normalizedValues.every(val => validRoles.indexOf(val) > -1);

@@ -44,9 +44,9 @@ if (isValid === true) {

node: attribute,
message: errorMessage
message: errorMessage,
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];
/**
* @fileoverview Enforce that elements that do not support ARIA roles, states and properties do not have those attributes.
* @fileoverview Enforce that elements that do not support ARIA roles,
* states and properties do not have those attributes.
* @author Ethan Cohen
*/
'use strict';

@@ -16,3 +16,4 @@ // ----------------------------------------------------------------------------

const errorMessage = invalidProp =>
`This element does not support ARIA roles, states and properties. Try removing the prop '${invalidProp}'.`;
`This element does not support ARIA roles, states and properties. \
Try removing the prop '${invalidProp}'.`;

@@ -33,14 +34,18 @@ module.exports = context => ({

node.attributes.forEach(prop => {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
if (invalidAttributes.indexOf(prop.name.name.toUpperCase()) > -1) {
context.report({
node,
message: errorMessage(prop.name.name)
message: errorMessage(prop.name.name),
});
}
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -14,7 +13,8 @@ // ----------------------------------------------------------------------------

const errorMessage = 'Links must not point to "#". Use a more descriptive href or use a button instead.';
const errorMessage = 'Links must not point to "#". ' +
'Use a more descriptive href or use a button instead.';
module.exports = context => ({
JSXOpeningElement: node => {
const typeCheck = [ 'a' ].concat(context.options[0]);
const typeCheck = ['a'].concat(context.options[0]);
const nodeType = elementType(node);

@@ -33,6 +33,6 @@

node,
message: errorMessage
message: errorMessage,
});
}
}
},
});

@@ -42,14 +42,14 @@

{
'oneOf': [
{ 'type': 'string' },
oneOf: [
{ type: 'string' },
{
'type': 'array',
'items': {
'type': 'string'
type: 'array',
items: {
type: 'string',
},
'minItems': 1,
'uniqueItems': true
}
]
}
minItems: 1,
uniqueItems: true,
},
],
},
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -16,3 +15,3 @@ // ----------------------------------------------------------------------------

JSXOpeningElement: node => {
const typeCheck = [ 'img' ].concat(context.options[0]);
const typeCheck = ['img'].concat(context.options[0]);
const nodeType = elementType(node);

@@ -27,3 +26,4 @@

const roleValue = getPropValue(roleProp);
const isPresentation = roleProp && typeof roleValue === 'string' && roleValue.toLowerCase() === 'presentation';
const isPresentation = roleProp && typeof roleValue === 'string'
&& roleValue.toLowerCase() === 'presentation';

@@ -40,3 +40,3 @@ if (isPresentation) {

node,
message: `${nodeType} elements must have an alt prop or use role="presentation".`
message: `${nodeType} elements must have an alt prop or use role="presentation".`,
});

@@ -58,5 +58,6 @@ return;

message:
`Invalid alt value for ${nodeType}. Use alt="" or role="presentation" for presentational images.`
`Invalid alt value for ${nodeType}. \
Use alt="" or role="presentation" for presentational images.`,
});
}
},
});

@@ -66,14 +67,14 @@

{
'oneOf': [
{ 'type': 'string' },
oneOf: [
{ type: 'string' },
{
'type': 'array',
'items': {
'type': 'string'
type: 'array',
items: {
type: 'string',
},
'minItems': 1,
'uniqueItems': true
}
]
}
minItems: 1,
uniqueItems: true,
},
],
},
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -18,11 +17,12 @@ // ----------------------------------------------------------------------------

'photo',
'picture'
'picture',
];
const errorMessage = 'Redundant alt attribute. Screen-readers already announce `img` tags as an image. ' +
'You don\'t need to use the words `image`, `photo,` or `picture` in the alt prop.';
const errorMessage = 'Redundant alt attribute. Screen-readers already announce ' +
'`img` tags as an image. You don\'t need to use the words `image`, ' +
'`photo,` or `picture` in the alt prop.';
const validTypes = [
'LITERAL',
'TEMPLATELITERAL'
'TEMPLATELITERAL',
];

@@ -44,3 +44,4 @@

// Only check literals, as we should not enforce variable names :P
const normalizedType = altProp.value && altProp.value.type.toUpperCase() === 'JSXEXPRESSIONCONTAINER' ?
const normalizedType = altProp.value &&
altProp.value.type.toUpperCase() === 'JSXEXPRESSIONCONTAINER' ?
altProp.value.expression.type.toUpperCase() :

@@ -63,3 +64,3 @@ altProp.value.type.toUpperCase();

node,
message: errorMessage
message: errorMessage,
});

@@ -70,7 +71,7 @@ }

}
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -19,3 +18,3 @@ // ----------------------------------------------------------------------------

JSXOpeningElement: node => {
const typeCheck = [ 'label' ].concat(context.options[0]);
const typeCheck = ['label'].concat(context.options[0]);
const nodeType = elementType(node);

@@ -35,6 +34,6 @@

node,
message: errorMessage
message: errorMessage,
});
}
}
},
});

@@ -44,14 +43,14 @@

{
'oneOf': [
{ 'type': 'string' },
oneOf: [
{ type: 'string' },
{
'type': 'array',
'items': {
'type': 'string'
type: 'array',
items: {
type: 'string',
},
'minItems': 1,
'uniqueItems': true
}
]
}
minItems: 1,
uniqueItems: true,
},
],
},
];

@@ -6,3 +6,2 @@ /**

*/
'use strict';

@@ -34,3 +33,3 @@ // ----------------------------------------------------------------------------

node,
message: mouseOverErrorMessage
message: mouseOverErrorMessage,
});

@@ -50,11 +49,11 @@ }

node,
message: mouseOutErrorMessage
message: mouseOutErrorMessage,
});
}
}
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -26,10 +25,10 @@ // ----------------------------------------------------------------------------

node,
message: errorMessage
message: errorMessage,
});
}
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -26,10 +25,10 @@ // ----------------------------------------------------------------------------

node,
message: errorMessage
message: errorMessage,
});
}
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -40,9 +39,9 @@ import isHiddenFromScreenReader from '../util/isHiddenFromScreenReader';

node,
message: errorMessage
message: errorMessage,
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -6,3 +6,2 @@ /**

*/
'use strict';

@@ -40,9 +39,9 @@ import isHiddenFromScreenReader from '../util/isHiddenFromScreenReader';

node,
message: errorMessage
message: errorMessage,
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];
/**
* @fileoverview Enforce that elements with ARIA roles must have all required attributes for that role.
* @fileoverview Enforce that elements with ARIA roles must
* have all required attributes for that role.
* @author Ethan Cohen
*/
'use strict';

@@ -29,3 +29,4 @@ // ----------------------------------------------------------------------------

// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the value isn't in the form of a literal.
// If value is null, then getLiteralAttributeValue is telling us
// that the value isn't in the form of a literal.
if (value === undefined || value === null) {

@@ -36,3 +37,4 @@ return;

const normalizedValues = String(value).toUpperCase().split(' ');
const validRoles = normalizedValues.filter(value => Object.keys(validRoleTypes).indexOf(value) > -1);
const validRoles = normalizedValues
.filter(val => Object.keys(validRoleTypes).indexOf(val) > -1);

@@ -43,3 +45,4 @@ validRoles.forEach(role => {

if (requiredProps.length > 0) {
const hasRequiredProps = requiredProps.every(prop => getProp(attribute.parent.attributes, prop));
const hasRequiredProps = requiredProps
.every(prop => getProp(attribute.parent.attributes, prop));

@@ -49,3 +52,3 @@ if (hasRequiredProps === false) {

node: attribute,
message: errorMessage(role.toLowerCase(), requiredProps)
message: errorMessage(role.toLowerCase(), requiredProps),
});

@@ -55,8 +58,7 @@ }

});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -6,3 +6,2 @@ /**

*/
'use strict';

@@ -20,3 +19,4 @@ // ----------------------------------------------------------------------------

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}. \
This role is implicit on the element ${tag}.`;
}

@@ -44,17 +44,22 @@

const propertySet = ROLES[roleValue.toUpperCase()].props;
const invalidAriaPropsForRole = Object.keys(ARIA).filter(attribute => propertySet.indexOf(attribute) === -1);
const invalidAriaPropsForRole = Object.keys(ARIA)
.filter(attribute => propertySet.indexOf(attribute) === -1);
node.attributes.forEach(prop => {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
if (invalidAriaPropsForRole.indexOf(prop.name.name.toUpperCase()) > -1) {
context.report({
node,
message: errorMessage(prop.name.name, roleValue, type, isImplicit)
message: errorMessage(prop.name.name, roleValue, type, isImplicit),
});
}
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -5,3 +5,2 @@ /**

*/
'use strict';

@@ -35,9 +34,9 @@ // ----------------------------------------------------------------------------

node: attribute,
message: errorMessage
message: errorMessage,
});
}
},
});
module.exports.schema = [
{ type: 'object' }
{ type: 'object' },
];

@@ -1,6 +0,3 @@

'use strict';
import editDistance from 'damerau-levenshtein';
// Minimum edit distance to be considered a good suggestion.

@@ -15,3 +12,5 @@ const THRESHOLD = 2;

const distances = dictionary.reduce((suggestions, dictionaryWord) => {
suggestions[dictionaryWord] = editDistance(word.toUpperCase(), dictionaryWord.toUpperCase()).steps;
const distance = editDistance(word.toUpperCase(), dictionaryWord.toUpperCase());
const { steps } = distance;
suggestions[dictionaryWord] = steps; // eslint-disable-line
return suggestions;

@@ -18,0 +17,0 @@ }, {});

@@ -1,3 +0,1 @@

'use strict';
import { getPropValue, getLiteralPropValue } from 'jsx-ast-utils';

@@ -4,0 +2,0 @@

@@ -39,4 +39,2 @@ import A from './a';

export default {

@@ -79,3 +77,3 @@ A,

THEAD,
UL
UL,
};

@@ -1,3 +0,1 @@

'use strict';
import { getProp, getPropValue, getLiteralPropValue } from 'jsx-ast-utils';

@@ -16,3 +14,3 @@

if (hidden && hidden.toUpperCase() == 'HIDDEN') {
if (hidden && hidden.toUpperCase() === 'HIDDEN') {
return true;

@@ -19,0 +17,0 @@ }

@@ -1,3 +0,1 @@

'use strict';
import { getProp, getPropValue, getLiteralPropValue } from 'jsx-ast-utils';

@@ -23,3 +21,3 @@ import getTabIndex from './getTabIndex';

select: () => true,
textarea: () => true
textarea: () => true,
};

@@ -26,0 +24,0 @@

/* eslint-env mocha */
'use strict';
import plugin from '../src';

@@ -18,3 +16,3 @@

plugin.rules[ruleName],
require(path.join('../src/rules', ruleName))
require(path.join('../src/rules', ruleName)) // eslint-disable-line global-require
);

@@ -21,0 +19,0 @@ });

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -21,4 +19,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -40,3 +38,3 @@

type: 'JSXAttribute',
message: `${message} Did you mean to use ${suggestions}?`
message: `${message} Did you mean to use ${suggestions}?`,
};

@@ -47,3 +45,3 @@ }

type: 'JSXAttribute',
message
message,
};

@@ -55,3 +53,3 @@ };

code: `<div ${prop.toLowerCase()}="foobar" />`,
parserOptions
parserOptions,
}));

@@ -68,9 +66,17 @@

{ code: '<div fooaria-hidden="true"></div>', parserOptions },
{ code: '<Bar baz />', parserOptions }
{ code: '<Bar baz />', parserOptions },
].concat(basicValidityTests),
invalid: [
{ code: '<div aria-="foobar" />', errors: [ errorMessage('aria-') ], parserOptions },
{ code: '<div aria-labeledby="foobar" />', errors: [ errorMessage('aria-labeledby') ], parserOptions },
{ code: '<div aria-skldjfaria-klajsd="foobar" />', errors: [ errorMessage('aria-skldjfaria-klajsd') ], parserOptions }
]
{ code: '<div aria-="foobar" />', errors: [errorMessage('aria-')], parserOptions },
{
code: '<div aria-labeledby="foobar" />',
errors: [errorMessage('aria-labeledby')],
parserOptions,
},
{
code: '<div aria-skldjfaria-klajsd="foobar" />',
errors: [errorMessage('aria-skldjfaria-klajsd')],
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -41,3 +39,4 @@

case 'tokenlist':
return `The value for ${name} must be a list of one or more tokens from the following: ${permittedValues}.`;
return `The value for ${name} must be a list of one or more \
tokens from the following: ${permittedValues}.`;
case 'boolean':

@@ -146,66 +145,130 @@ case 'string':

{ code: '<div aria-relevant={foo} />', parserOptions },
{ code: '<div aria-relevant={foo.bar} />', parserOptions }
{ code: '<div aria-relevant={foo.bar} />', parserOptions },
],
invalid: [
// BOOLEAN
{ code: '<div aria-hidden={undefined} />', errors: [ errorMessage('aria-hidden') ], parserOptions },
{ code: '<div aria-hidden="yes" />', errors: [ errorMessage('aria-hidden') ], parserOptions },
{ code: '<div aria-hidden="no" />', errors: [ errorMessage('aria-hidden') ], parserOptions },
{ code: '<div aria-hidden={1234} />', errors: [ errorMessage('aria-hidden') ], parserOptions },
{ code: '<div aria-hidden={`${abc}`} />', errors: [ errorMessage('aria-hidden') ], parserOptions },
{
code: '<div aria-hidden={undefined} />',
errors: [errorMessage('aria-hidden')],
parserOptions,
},
{ code: '<div aria-hidden="yes" />', errors: [errorMessage('aria-hidden')], parserOptions },
{ code: '<div aria-hidden="no" />', errors: [errorMessage('aria-hidden')], parserOptions },
{ code: '<div aria-hidden={1234} />', errors: [errorMessage('aria-hidden')], parserOptions },
{
code: '<div aria-hidden={`${abc}`} />',
errors: [errorMessage('aria-hidden')],
parserOptions,
},
// STRING
{ code: '<div aria-label={undefined} />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label={true} />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label={false} />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label={1234} />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label={!true} />', errors: [ errorMessage('aria-label') ], parserOptions },
{ code: '<div aria-label={undefined} />', errors: [errorMessage('aria-label')], parserOptions },
{ code: '<div aria-label />', errors: [errorMessage('aria-label')], parserOptions },
{ code: '<div aria-label={true} />', errors: [errorMessage('aria-label')], parserOptions },
{ code: '<div aria-label={false} />', errors: [errorMessage('aria-label')], parserOptions },
{ code: '<div aria-label={1234} />', errors: [errorMessage('aria-label')], parserOptions },
{ code: '<div aria-label={!true} />', errors: [errorMessage('aria-label')], parserOptions },
// TRISTATE
{ code: '<div aria-checked={undefined} />', errors: [ errorMessage('aria-checked') ], parserOptions },
{ code: '<div aria-checked="yes" />', errors: [ errorMessage('aria-checked') ], parserOptions },
{ code: '<div aria-checked="no" />', errors: [ errorMessage('aria-checked') ], parserOptions },
{ code: '<div aria-checked={1234} />', errors: [ errorMessage('aria-checked') ], parserOptions },
{ code: '<div aria-checked={`${abc}`} />', errors: [ errorMessage('aria-checked') ], parserOptions },
{
code: '<div aria-checked={undefined} />',
errors: [errorMessage('aria-checked')],
parserOptions,
},
{ code: '<div aria-checked="yes" />', errors: [errorMessage('aria-checked')], parserOptions },
{ code: '<div aria-checked="no" />', errors: [errorMessage('aria-checked')], parserOptions },
{ code: '<div aria-checked={1234} />', errors: [errorMessage('aria-checked')], parserOptions },
{
code: '<div aria-checked={`${abc}`} />',
errors: [errorMessage('aria-checked')],
parserOptions,
},
// INTEGER
{ code: '<div aria-level={undefined} />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level="yes" />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level="no" />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level={`abc`} />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level={true} />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level={"false"} />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level={!"false"} />', errors: [ errorMessage('aria-level') ], parserOptions },
{ code: '<div aria-level={undefined} />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level="yes" />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level="no" />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level={`abc`} />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level={true} />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level={"false"} />', errors: [errorMessage('aria-level')], parserOptions },
{ code: '<div aria-level={!"false"} />', errors: [errorMessage('aria-level')], parserOptions },
// NUMBER
{ code: '<div aria-valuemax={undefined} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax="yes" />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax="no" />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax={`abc`} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax={true} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax={"false"} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{ code: '<div aria-valuemax={!"false"} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
{
code: '<div aria-valuemax={undefined} />',
errors: [errorMessage('aria-valuemax')],
parserOptions,
},
{ code: '<div aria-valuemax="yes" />', errors: [errorMessage('aria-valuemax')], parserOptions },
{ code: '<div aria-valuemax="no" />', errors: [errorMessage('aria-valuemax')], parserOptions },
{
code: '<div aria-valuemax={`abc`} />',
errors: [errorMessage('aria-valuemax')],
parserOptions,
},
{
code: '<div aria-valuemax={true} />',
errors: [errorMessage('aria-valuemax')],
parserOptions,
},
{ code: '<div aria-valuemax />', errors: [errorMessage('aria-valuemax')], parserOptions },
{
code: '<div aria-valuemax={"false"} />',
errors: [errorMessage('aria-valuemax')],
parserOptions,
},
{
code: '<div aria-valuemax={!"false"} />',
errors: [errorMessage('aria-valuemax')],
parserOptions,
},
// TOKEN
{ code: '<div aria-sort="" />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort="descnding" />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort={undefined} />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort={true} />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort={"false"} />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort="ascending descending" />', errors: [ errorMessage('aria-sort') ], parserOptions },
{ code: '<div aria-sort="" />', errors: [errorMessage('aria-sort')], parserOptions },
{ code: '<div aria-sort="descnding" />', errors: [errorMessage('aria-sort')], parserOptions },
{ code: '<div aria-sort />', errors: [errorMessage('aria-sort')], parserOptions },
{ code: '<div aria-sort={undefined} />', errors: [errorMessage('aria-sort')], parserOptions },
{ code: '<div aria-sort={true} />', errors: [errorMessage('aria-sort')], parserOptions },
{ code: '<div aria-sort={"false"} />', errors: [errorMessage('aria-sort')], parserOptions },
{
code: '<div aria-sort="ascending descending" />',
errors: [errorMessage('aria-sort')],
parserOptions,
},
// TOKENLIST
{ code: '<div aria-relevant="" />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant="foobar" />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant={undefined} />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant={true} />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant={"false"} />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant="additions removalss" />', errors: [ errorMessage('aria-relevant') ], parserOptions },
{ code: '<div aria-relevant="additions removalss " />', errors: [ errorMessage('aria-relevant') ], parserOptions }
]
{ code: '<div aria-relevant="" />', errors: [errorMessage('aria-relevant')], parserOptions },
{
code: '<div aria-relevant="foobar" />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
{ code: '<div aria-relevant />', errors: [errorMessage('aria-relevant')], parserOptions },
{
code: '<div aria-relevant={undefined} />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
{
code: '<div aria-relevant={true} />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
{
code: '<div aria-relevant={"false"} />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
{
code: '<div aria-relevant="additions removalss" />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
{
code: '<div aria-relevant="additions removalss " />',
errors: [errorMessage('aria-relevant')],
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -15,2 +13,4 @@ // Requirements

import { RuleTester } from 'eslint';
import ROLES from '../../../src/util/attributes/role';
import assign from 'object-assign';

@@ -20,4 +20,4 @@ const parserOptions = {

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,7 +33,5 @@

message: 'Elements with ARIA roles must use a valid, non-abstract ARIA role.',
type: 'JSXAttribute'
type: 'JSXAttribute',
};
import ROLES from '../../../src/util/attributes/role';
const validRoles = Object.keys(ROLES).filter(role => ROLES[role].abstract === false);

@@ -44,3 +42,3 @@ const invalidRoles = Object.keys(ROLES).filter(role => ROLES[role].abstract === true);

code: `<div role="${role.toLowerCase()}" />`,
parserOptions
parserOptions,
}));

@@ -50,4 +48,5 @@

const invalidTests = createTests(invalidRoles).map(test => {
test.errors = [ errorMessage ];
return test;
const invalidTest = assign({}, test);
invalidTest.errors = [errorMessage];
return invalidTest;
});

@@ -66,16 +65,16 @@

{ code: '<div role="doc-appendix doc-bibliography" />', parserOptions },
{ code: '<Bar baz />', parserOptions }
{ code: '<Bar baz />', parserOptions },
].concat(validTests),
invalid: [
{ code: '<div role="foobar" />', errors: [ errorMessage ], parserOptions },
{ code: '<div role="datepicker"></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role="range"></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role=""></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role="tabpanel row foobar"></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role="tabpanel row range"></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role="doc-endnotes range"></div>', errors: [ errorMessage ], parserOptions },
{ code: '<div role />', errors: [ errorMessage ], parserOptions },
{ code: '<div role={null}></div>', errors: [ errorMessage ], parserOptions }
].concat(invalidTests)
{ code: '<div role="foobar" />', errors: [errorMessage], parserOptions },
{ code: '<div role="datepicker"></div>', errors: [errorMessage], parserOptions },
{ code: '<div role="range"></div>', errors: [errorMessage], parserOptions },
{ code: '<div role=""></div>', errors: [errorMessage], parserOptions },
{ code: '<div role="tabpanel row foobar"></div>', errors: [errorMessage], parserOptions },
{ code: '<div role="tabpanel row range"></div>', errors: [errorMessage], parserOptions },
{ code: '<div role="doc-endnotes range"></div>', errors: [errorMessage], parserOptions },
{ code: '<div role />', errors: [errorMessage], parserOptions },
{ code: '<div role={null}></div>', errors: [errorMessage], parserOptions },
].concat(invalidTests),
});
/**
* @fileoverview Enforce that elements that do not support ARIA roles, states and properties do not have those attributes.
* @fileoverview Enforce that elements that do not support ARIA roles,
* states and properties do not have those attributes.
* @author Ethan Cohen
*/
'use strict';
// -----------------------------------------------------------------------------

@@ -18,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -32,4 +31,5 @@

const errorMessage = invalidProp => ({
message: `This element does not support ARIA roles, states and properties. Try removing the prop '${invalidProp}'.`,
type: 'JSXOpeningElement'
message: `This element does not support ARIA roles, states and properties. \
Try removing the prop '${invalidProp}'.`,
type: 'JSXOpeningElement',
});

@@ -44,3 +44,3 @@

code: `<${element} ${role} />`,
parserOptions
parserOptions,
};

@@ -55,3 +55,3 @@ });

code: `<${element} ${aria} />`,
parserOptions
parserOptions,
};

@@ -64,5 +64,5 @@ });

.map(reservedElem => ({
code: `<${reservedElem} role />`,
errors: [ errorMessage('role') ],
parserOptions
code: `<${reservedElem} role {...props} />`,
errors: [errorMessage('role')],
parserOptions,
}));

@@ -73,5 +73,5 @@

.map(reservedElem => ({
code: `<${reservedElem} aria-hidden />`,
errors: [ errorMessage('aria-hidden') ],
parserOptions
code: `<${reservedElem} aria-hidden {...props} />`,
errors: [errorMessage('aria-hidden')],
parserOptions,
}));

@@ -81,3 +81,3 @@

valid: roleValidityTests.concat(ariaValidityTests),
invalid: invalidRoleValidityTests.concat(invalidAriaValidityTests)
invalid: invalidRoleValidityTests.concat(invalidAriaValidityTests),
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -32,7 +30,7 @@

message: 'Links must not point to "#". Use a more descriptive href or use a button instead.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};
const string = [ 'Link' ];
const array = [ [ 'Anchor', 'Link' ] ];
const string = ['Link'];
const array = [['Anchor', 'Link']];

@@ -95,23 +93,38 @@ ruleTester.run('href-no-hash', rule, {

{ code: '<Link href={"foo"}/>', options: array, parserOptions },
{ code: '<Link href="#foo" />', options: array, parserOptions }
{ code: '<Link href="#foo" />', options: array, parserOptions },
],
invalid: [
// DEFAULT ELEMENT 'a' TESTS
{ code: '<a href="#" />', errors: [ expectedError ], parserOptions },
{ code: '<a href={"#"} />', errors: [ expectedError ], parserOptions },
{ code: '<a href={`#${undefined}`} />', errors: [ expectedError ], parserOptions },
{ code: '<a href="#" />', errors: [expectedError], parserOptions },
{ code: '<a href={"#"} />', errors: [expectedError], parserOptions },
{ code: '<a href={`#${undefined}`} />', errors: [expectedError], parserOptions },
// CUSTOM ELEMENT TEST FOR STRING OPTION
{ code: '<Link href="#" />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Link href={"#"} />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Link href={`#${undefined}`} />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Link href="#" />', errors: [expectedError], options: string, parserOptions },
{ code: '<Link href={"#"} />', errors: [expectedError], options: string, parserOptions },
{
code: '<Link href={`#${undefined}`} />',
errors: [expectedError],
options: string,
parserOptions,
},
// CUSTOM ELEMENT TEST FOR ARRAY OPTION
{ code: '<Link href="#" />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Link href={"#"} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Link href={`#${undefined}`} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Anchor href="#" />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Anchor href={"#"} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Anchor href={`#${undefined}`} />', errors: [ expectedError ], options: array, parserOptions }
]
{ code: '<Link href="#" />', errors: [expectedError], options: array, parserOptions },
{ code: '<Link href={"#"} />', errors: [expectedError], options: array, parserOptions },
{
code: '<Link href={`#${undefined}`} />',
errors: [expectedError],
options: array,
parserOptions,
},
{ code: '<Anchor href="#" />', errors: [expectedError], options: array, parserOptions },
{ code: '<Anchor href={"#"} />', errors: [expectedError], options: array, parserOptions },
{
code: '<Anchor href={`#${undefined}`} />',
errors: [expectedError],
options: array,
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -32,12 +30,13 @@

message: `${type} elements must have an alt prop or use role="presentation".`,
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
});
const altValueError = type => ({
message: `Invalid alt value for ${type}. Use alt="" or role="presentation" for presentational images.`,
type: 'JSXOpeningElement'
message: `Invalid alt value for ${type}. \
Use alt="" or role="presentation" for presentational images.`,
type: 'JSXOpeningElement',
});
const string = [ 'Avatar' ];
const array = [ [ 'Thumbnail', 'Image' ] ];
const string = ['Avatar'];
const array = [['Thumbnail', 'Image']];

@@ -127,13 +126,13 @@

{ code: '<IMAGE />', options: array, parserOptions },
{ code: '<Image alt={alt || "foo" } />', options: array, parserOptions }
{ code: '<Image alt={alt || "foo" } />', options: array, parserOptions },
],
invalid: [
// DEFAULT ELEMENT 'img' TESTS
{ code: '<img />;', errors: [ missingPropError('img') ], parserOptions },
{ code: '<img alt />;', errors: [ altValueError('img') ], parserOptions },
{ code: '<img alt={undefined} />;', errors: [ altValueError('img') ], parserOptions },
{ code: '<img src="xyz" />', errors: [ missingPropError('img') ], parserOptions },
{ code: '<img role />', errors: [ missingPropError('img') ], parserOptions },
{ code: '<img {...this.props} />', errors: [ missingPropError('img') ], parserOptions },
{ code: '<img alt={false || false} />', errors: [ altValueError('img') ], parserOptions },
{ code: '<img />;', errors: [missingPropError('img')], parserOptions },
{ code: '<img alt />;', errors: [altValueError('img')], parserOptions },
{ code: '<img alt={undefined} />;', errors: [altValueError('img')], parserOptions },
{ code: '<img src="xyz" />', errors: [missingPropError('img')], parserOptions },
{ code: '<img role />', errors: [missingPropError('img')], parserOptions },
{ code: '<img {...this.props} />', errors: [missingPropError('img')], parserOptions },
{ code: '<img alt={false || false} />', errors: [altValueError('img')], parserOptions },

@@ -143,33 +142,77 @@ // CUSTOM ELEMENT TESTS FOR STRING OPTION

code: '<Avatar />;',
errors: [ missingPropError('Avatar') ],
errors: [missingPropError('Avatar')],
options: string,
parserOptions
parserOptions,
},
{ code: '<Avatar alt />;', errors: [ altValueError('Avatar') ], options: string, parserOptions },
{ code: '<Avatar alt={undefined} />;', errors: [ altValueError('Avatar') ], options: string, parserOptions },
{ code: '<Avatar src="xyz" />', errors: [ missingPropError('Avatar') ], options: string, parserOptions },
{ code: '<Avatar {...this.props} />', errors: [ missingPropError('Avatar') ], options: string, parserOptions },
{ code: '<Avatar alt />;', errors: [altValueError('Avatar')], options: string, parserOptions },
{
code: '<Avatar alt={undefined} />;',
errors: [altValueError('Avatar')],
options: string,
parserOptions,
},
{
code: '<Avatar src="xyz" />',
errors: [missingPropError('Avatar')],
options: string, parserOptions,
},
{
code: '<Avatar {...this.props} />',
errors: [missingPropError('Avatar')],
options: string,
parserOptions,
},
// CUSTOM ELEMENT TESTS FOR ARRAY OPTION TESTS
{ code: '<Thumbnail />;', errors: [ missingPropError('Thumbnail') ], options: array, parserOptions },
{ code: '<Thumbnail alt />;', errors: [ altValueError('Thumbnail') ], options: array, parserOptions },
{
code: '<Thumbnail />;',
errors: [missingPropError('Thumbnail')],
options: array,
parserOptions,
},
{
code: '<Thumbnail alt />;',
errors: [altValueError('Thumbnail')],
options: array,
parserOptions,
},
{
code: '<Thumbnail alt={undefined} />;',
errors: [ altValueError('Thumbnail') ],
errors: [altValueError('Thumbnail')],
options: array,
parserOptions
parserOptions,
},
{ code: '<Thumbnail src="xyz" />', errors: [ missingPropError('Thumbnail') ], options: array, parserOptions },
{
code: '<Thumbnail src="xyz" />',
errors: [missingPropError('Thumbnail')],
options: array,
parserOptions,
},
{
code: '<Thumbnail {...this.props} />',
errors: [ missingPropError('Thumbnail') ],
errors: [missingPropError('Thumbnail')],
options: array,
parserOptions
parserOptions,
},
{ code: '<Image />;', errors: [ missingPropError('Image') ], options: array, parserOptions },
{ code: '<Image alt />;', errors: [ altValueError('Image') ], options: array, parserOptions },
{ code: '<Image alt={undefined} />;', errors: [ altValueError('Image') ], options: array, parserOptions },
{ code: '<Image src="xyz" />', errors: [ missingPropError('Image') ], options: array, parserOptions },
{ code: '<Image {...this.props} />', errors: [ missingPropError('Image') ], options: array, parserOptions }
]
{ code: '<Image />;', errors: [missingPropError('Image')], options: array, parserOptions },
{ code: '<Image alt />;', errors: [altValueError('Image')], options: array, parserOptions },
{
code: '<Image alt={undefined} />;',
errors: [altValueError('Image')],
options: array,
parserOptions,
},
{
code: '<Image src="xyz" />',
errors: [missingPropError('Image')],
options: array,
parserOptions,
},
{
code: '<Image {...this.props} />',
errors: [missingPropError('Image')],
options: array,
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,3 +31,3 @@

'You don\'t need to use the words `image`, `photo,` or `picture` in the alt prop.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -62,25 +60,61 @@

{ code: '<UX.Layout>test</UX.Layout>', parserOptions },
{ code: '<img alt={imageAlt} />', parserOptions }
{ code: '<img alt={imageAlt} />', parserOptions },
],
invalid: [
{ code: '<img alt="Photo of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="Picture of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="Image of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="PhOtO of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt={"photo"} />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="piCTUre of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="imAGE of friend." />;', errors: [ expectedError ], parserOptions },
{ code: '<img alt="photo of cool person" aria-hidden={false} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="picture of cool person" aria-hidden={false} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="image of cool person" aria-hidden={false} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="photo" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="image" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt="picture" {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`picture doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`photo doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`image doing ${things}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`picture doing ${picture}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`photo doing ${photo}`} {...this.props} />', errors: [ expectedError ], parserOptions },
{ code: '<img alt={`image doing ${image}`} {...this.props} />', errors: [ expectedError ], parserOptions }
]
{ code: '<img alt="Photo of friend." />;', errors: [expectedError], parserOptions },
{ code: '<img alt="Picture of friend." />;', errors: [expectedError], parserOptions },
{ code: '<img alt="Image of friend." />;', errors: [expectedError], parserOptions },
{ code: '<img alt="PhOtO of friend." />;', errors: [expectedError], parserOptions },
{ code: '<img alt={"photo"} />;', errors: [expectedError], parserOptions },
{ code: '<img alt="piCTUre of friend." />;', errors: [expectedError], parserOptions },
{ code: '<img alt="imAGE of friend." />;', errors: [expectedError], parserOptions },
{
code: '<img alt="photo of cool person" aria-hidden={false} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt="picture of cool person" aria-hidden={false} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt="image of cool person" aria-hidden={false} />',
errors: [expectedError],
parserOptions,
},
{ code: '<img alt="photo" {...this.props} />', errors: [expectedError], parserOptions },
{ code: '<img alt="image" {...this.props} />', errors: [expectedError], parserOptions },
{ code: '<img alt="picture" {...this.props} />', errors: [expectedError], parserOptions },
{
code: '<img alt={`picture doing ${things}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt={`photo doing ${things}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt={`image doing ${things}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt={`picture doing ${picture}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt={`photo doing ${photo}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
{
code: '<img alt={`image doing ${image}`} {...this.props} />',
errors: [expectedError],
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -16,7 +14,7 @@ // Requirements

const parserOptions = {
const parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,7 +31,7 @@

'programmatically associated with the control using htmlFor',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};
const string = [ 'Label' ];
const array = [ [ 'Label', 'Descriptor' ] ];
const string = ['Label'];
const array = [['Label', 'Descriptor']];

@@ -73,31 +71,81 @@ ruleTester.run('label-has-for', rule, {

{ code: '<div />', options: array, parserOptions },
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: array, parserOptions }
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: array, parserOptions },
],
invalid: [
// DEFAULT ELEMENT 'label' TESTS
{ code: '<label id="foo" />', errors: [ expectedError ], parserOptions },
{ code: '<label htmlFor={undefined} />', errors: [ expectedError ], parserOptions },
{ code: '<label htmlFor={`${undefined}`} />', errors: [ expectedError ], parserOptions },
{ code: '<label>First Name</label>', errors: [ expectedError ], parserOptions },
{ code: '<label {...props}>Foo</label>', errors: [ expectedError ], parserOptions },
{ code: '<label id="foo" />', errors: [expectedError], parserOptions },
{ code: '<label htmlFor={undefined} />', errors: [expectedError], parserOptions },
{ code: '<label htmlFor={`${undefined}`} />', errors: [expectedError], parserOptions },
{ code: '<label>First Name</label>', errors: [expectedError], parserOptions },
{ code: '<label {...props}>Foo</label>', errors: [expectedError], parserOptions },
// CUSTOM ELEMENT STRING OPTION TESTS
{ code: '<Label id="foo" />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Label htmlFor={undefined} />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Label htmlFor={`${undefined}`} />', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Label>First Name</Label>', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Label {...props}>Foo</Label>', errors: [ expectedError ], options: string, parserOptions },
{ code: '<Label id="foo" />', errors: [expectedError], options: string, parserOptions },
{
code: '<Label htmlFor={undefined} />',
errors: [expectedError],
options: string,
parserOptions,
},
{
code: '<Label htmlFor={`${undefined}`} />',
errors: [expectedError],
options: string,
parserOptions,
},
{ code: '<Label>First Name</Label>', errors: [expectedError], options: string, parserOptions },
{
code: '<Label {...props}>Foo</Label>',
errors: [expectedError],
options: string,
parserOptions,
},
// CUSTOM ELEMENT ARRAY OPTION TESTS
{ code: '<Label id="foo" />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Label htmlFor={undefined} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Label htmlFor={`${undefined}`} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Label>First Name</Label>', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Label {...props}>Foo</Label>', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Descriptor id="foo" />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Descriptor htmlFor={undefined} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Descriptor htmlFor={`${undefined}`} />', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Descriptor>First Name</Descriptor>', errors: [ expectedError ], options: array, parserOptions },
{ code: '<Descriptor {...props}>Foo</Descriptor>', errors: [ expectedError ], options: array, parserOptions }
]
{ code: '<Label id="foo" />', errors: [expectedError], options: array, parserOptions },
{
code: '<Label htmlFor={undefined} />',
errors: [expectedError],
options: array,
parserOptions,
},
{
code: '<Label htmlFor={`${undefined}`} />',
errors: [expectedError],
options: array,
parserOptions,
},
{ code: '<Label>First Name</Label>', errors: [expectedError], options: array, parserOptions },
{
code: '<Label {...props}>Foo</Label>',
errors: [expectedError],
options: array,
parserOptions,
},
{ code: '<Descriptor id="foo" />', errors: [expectedError], options: array, parserOptions },
{
code: '<Descriptor htmlFor={undefined} />',
errors: [expectedError],
options: array,
parserOptions,
},
{
code: '<Descriptor htmlFor={`${undefined}`} />',
errors: [expectedError],
options: array,
parserOptions,
},
{
code: '<Descriptor>First Name</Descriptor>',
errors: [expectedError],
options: array,
parserOptions,
},
{
code: '<Descriptor {...props}>Foo</Descriptor>',
errors: [expectedError],
options: array,
parserOptions,
},
],
});

@@ -7,4 +7,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -20,4 +18,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,7 +31,7 @@

message: 'onMouseOver must be accompanied by onFocus for accessibility.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};
const mouseOutError = {
message: 'onMouseOut must be accompanied by onBlur for accessibility.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -44,5 +42,11 @@

{ code: '<div onMouseOver={() => void 0} onFocus={() => void 0} />;', parserOptions },
{ code: '<div onMouseOver={() => void 0} onFocus={() => void 0} {...props} />;', parserOptions },
{
code: '<div onMouseOver={() => void 0} onFocus={() => void 0} {...props} />;',
parserOptions,
},
{ code: '<div onMouseOver={handleMouseOver} onFocus={handleFocus} />;', parserOptions },
{ code: '<div onMouseOver={handleMouseOver} onFocus={handleFocus} {...props} />;', parserOptions },
{
code: '<div onMouseOver={handleMouseOver} onFocus={handleFocus} {...props} />;',
parserOptions,
},
{ code: '<div />;', parserOptions },

@@ -52,12 +56,28 @@ { code: '<div onMouseOut={() => void 0} onBlur={() => void 0} />', parserOptions },

{ code: '<div onMouseOut={handleMouseOut} onBlur={handleOnBlur} />', parserOptions },
{ code: '<div onMouseOut={handleMouseOut} onBlur={handleOnBlur} {...props} />', parserOptions }
{ code: '<div onMouseOut={handleMouseOut} onBlur={handleOnBlur} {...props} />', parserOptions },
],
invalid: [
{ code: '<div onMouseOver={() => void 0} />;', errors: [ mouseOverError ], parserOptions },
{ code: '<div onMouseOut={() => void 0} />', errors: [ mouseOutError ], parserOptions },
{ code: '<div onMouseOver={() => void 0} onFocus={undefined} />;', errors: [ mouseOverError ], parserOptions },
{ code: '<div onMouseOut={() => void 0} onBlur={undefined} />', errors: [ mouseOutError ], parserOptions },
{ code: '<div onMouseOver={() => void 0} {...props} />', errors: [ mouseOverError ], parserOptions },
{ code: '<div onMouseOut={() => void 0} {...props} />', errors: [ mouseOutError ], parserOptions }
]
{ code: '<div onMouseOver={() => void 0} />;', errors: [mouseOverError], parserOptions },
{ code: '<div onMouseOut={() => void 0} />', errors: [mouseOutError], parserOptions },
{
code: '<div onMouseOver={() => void 0} onFocus={undefined} />;',
errors: [mouseOverError],
parserOptions,
},
{
code: '<div onMouseOut={() => void 0} onBlur={undefined} />',
errors: [mouseOutError],
parserOptions,
},
{
code: '<div onMouseOver={() => void 0} {...props} />',
errors: [mouseOverError],
parserOptions,
},
{
code: '<div onMouseOut={() => void 0} {...props} />',
errors: [mouseOutError],
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -34,3 +32,3 @@

'and keyboard only users create a11y complications.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -44,15 +42,19 @@

{ code: '<div accessKey={`${undefined}`} />', parserOptions },
{ code: '<div accessKey={`${undefined}${undefined}`} />', parserOptions }
{ code: '<div accessKey={`${undefined}${undefined}`} />', parserOptions },
],
invalid: [
{ code: '<div accesskey="h" />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey="h" />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey="h" {...props} />', errors: [ expectedError ], parserOptions },
{ code: '<div acCesSKeY="y" />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey={"y"} />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey={`${y}`} />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey={`${undefined}y${undefined}`} />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey={`This is ${bad}`} />', errors: [ expectedError ], parserOptions },
{ code: '<div accessKey={accessKey} />', errors: [ expectedError ], parserOptions }
]
{ code: '<div accesskey="h" />', errors: [expectedError], parserOptions },
{ code: '<div accessKey="h" />', errors: [expectedError], parserOptions },
{ code: '<div accessKey="h" {...props} />', errors: [expectedError], parserOptions },
{ code: '<div acCesSKeY="y" />', errors: [expectedError], parserOptions },
{ code: '<div accessKey={"y"} />', errors: [expectedError], parserOptions },
{ code: '<div accessKey={`${y}`} />', errors: [expectedError], parserOptions },
{
code: '<div accessKey={`${undefined}y${undefined}`} />',
errors: [expectedError],
parserOptions,
},
{ code: '<div accessKey={`This is ${bad}`} />', errors: [expectedError], parserOptions },
{ code: '<div accessKey={accessKey} />', errors: [expectedError], parserOptions },
],
});

@@ -6,3 +6,2 @@ /**

'use strict';

@@ -16,7 +15,7 @@ // -----------------------------------------------------------------------------

const parserOptions = {
const parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,3 +32,3 @@

'causes no negative consequences for keyboard only or screen reader users.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -43,10 +42,10 @@

{ code: '<div onBlur={() => {}} onChange={() => {}} />;', parserOptions },
{ code: '<div {...props} />', parserOptions }
{ code: '<div {...props} />', parserOptions },
],
invalid: [
{ code: '<div onChange={() => {}} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onChange={handleOnChange} />;', errors: [ expectedError ], parserOptions },
{ code: '<input onChange={() => {}} />', errors: [ expectedError ], parserOptions },
{ code: '<input onChange={() => {}} {...props} />', errors: [ expectedError ], parserOptions }
]
{ code: '<div onChange={() => {}} />;', errors: [expectedError], parserOptions },
{ code: '<div onChange={handleOnChange} />;', errors: [expectedError], parserOptions },
{ code: '<input onChange={() => {}} />', errors: [expectedError], parserOptions },
{ code: '<input onChange={() => {}} {...props} />', errors: [expectedError], parserOptions },
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -16,7 +14,7 @@ // Requirements

const parserOptions = {
const parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -30,7 +28,7 @@

const expectedError = {
const expectedError = {
message: 'Elements with onClick handlers must be focusable. ' +
'Either set the tabIndex property to a valid value (usually 0), ' +
'or use an element type which is inherently focusable such as `button`.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -71,28 +69,71 @@

{ code: '<span onClick="doSomething();" tabIndex="-1">Click me too!</span>', parserOptions },
{ code: '<a href="javascript:void(0);" onClick="doSomething();">Click ALL the things!</a>', parserOptions },
{
code: '<a href="javascript:void(0);" onClick="doSomething();">Click ALL the things!</a>',
parserOptions,
},
{ code: '<Foo.Bar onClick={() => void 0} aria-hidden={false} />;', parserOptions },
{ code: '<Input onClick={() => void 0} type="hidden" />;', parserOptions }
{ code: '<Input onClick={() => void 0} type="hidden" />;', parserOptions },
],
invalid: [
{ code: '<span onClick="submitForm();">Submit</span>', errors: [ expectedError ], parserOptions },
{ code: '<span onClick="submitForm();" tabIndex={undefined}>Submit</span>', errors: [ expectedError ], parserOptions },
{ code: '<span onClick="submitForm();" tabIndex="bad">Submit</span>', errors: [ expectedError ], parserOptions },
{ code: '<a onClick="showNextPage();">Next page</a>', errors: [ expectedError ], parserOptions },
{ code: '<a onClick="showNextPage();" tabIndex={undefined}>Next page</a>', errors: [ expectedError ], parserOptions },
{ code: '<a onClick="showNextPage();" tabIndex="bad">Next page</a>', errors: [ expectedError ], parserOptions },
{ code: '<a onClick={() => void 0} />', errors: [ expectedError ], parserOptions },
{ code: '<area onClick={() => void 0} className="foo" />', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} tabIndex={undefined} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} tabIndex="bad" />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} role={undefined} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} aria-hidden={false} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} {...props} />;', errors: [ expectedError ], parserOptions },
{ code: '<section onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<main onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<article onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<header onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<footer onClick={() => void 0} />;', errors: [ expectedError ], parserOptions }
]
{ code: '<span onClick="submitForm();">Submit</span>', errors: [expectedError], parserOptions },
{
code: '<span onClick="submitForm();" tabIndex={undefined}>Submit</span>',
errors: [expectedError],
parserOptions,
},
{
code: '<span onClick="submitForm();" tabIndex="bad">Submit</span>',
errors: [expectedError],
parserOptions,
},
{ code: '<a onClick="showNextPage();">Next page</a>', errors: [expectedError], parserOptions },
{
code: '<a onClick="showNextPage();" tabIndex={undefined}>Next page</a>',
errors: [expectedError],
parserOptions,
},
{
code: '<a onClick="showNextPage();" tabIndex="bad">Next page</a>',
errors: [expectedError],
parserOptions,
},
{
code: '<a onClick={() => void 0} />',
errors: [expectedError],
parserOptions,
},
{
code: '<area onClick={() => void 0} className="foo" />',
errors: [expectedError],
parserOptions,
},
{ code: '<div onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{
code: '<div onClick={() => void 0} tabIndex={undefined} />;',
errors: [expectedError],
parserOptions,
},
{
code: '<div onClick={() => void 0} tabIndex="bad" />;',
errors: [expectedError],
parserOptions,
},
{
code: '<div onClick={() => void 0} role={undefined} />;',
errors: [expectedError],
parserOptions,
},
{
code: '<div onClick={() => void 0} aria-hidden={false} />;',
errors: [expectedError],
parserOptions,
},
{ code: '<div onClick={() => void 0} {...props} />;', errors: [expectedError], parserOptions },
{ code: '<section onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<main onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<article onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<header onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<footer onClick={() => void 0} />;', errors: [expectedError], parserOptions },
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -16,7 +14,7 @@ // Requirements

const parserOptions = {
const parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -30,6 +28,6 @@

const expectedError = {
const expectedError = {
message: 'Visible, non-interactive elements with click ' +
'handlers must have role attribute.',
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
};

@@ -48,3 +46,6 @@

{ code: '<div onClick={() => void 0} role="button" aria-hidden={false} />;', parserOptions },
{ code: '<div onClick={() => void 0} role="button" aria-hidden={undefined} />;', parserOptions },
{
code: '<div onClick={() => void 0} role="button" aria-hidden={undefined} />;',
parserOptions,
},
{ code: '<input type="text" onClick={() => void 0} />', parserOptions },

@@ -62,16 +63,24 @@ { code: '<input onClick={() => void 0} />', parserOptions },

{ code: '<TestComponent onClick={doFoo} />', parserOptions },
{ code: '<Button onClick={doFoo} />', parserOptions }
{ code: '<Button onClick={doFoo} />', parserOptions },
],
invalid: [
{ code: '<div onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} role={undefined} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} {...props} />;', errors: [ expectedError ], parserOptions },
{ code: '<section onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<main onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<article onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<header onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<footer onClick={() => void 0} />;', errors: [ expectedError ], parserOptions },
{ code: '<div onClick={() => void 0} aria-hidden={false} />;', errors: [ expectedError ], parserOptions },
{ code: '<a onClick={() => void 0} />', errors: [ expectedError ], parserOptions }
]
{ code: '<div onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{
code: '<div onClick={() => void 0} role={undefined} />;',
errors: [expectedError],
parserOptions,
},
{ code: '<div onClick={() => void 0} {...props} />;', errors: [expectedError], parserOptions },
{ code: '<section onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<main onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<article onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<header onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{ code: '<footer onClick={() => void 0} />;', errors: [expectedError], parserOptions },
{
code: '<div onClick={() => void 0} aria-hidden={false} />;',
errors: [expectedError],
parserOptions,
},
{ code: '<a onClick={() => void 0} />', errors: [expectedError], parserOptions },
],
});
/**
* @fileoverview Enforce that elements with ARIA roles must have all required attributes for that role.
* @fileoverview Enforce that elements with ARIA roles must
* have all required attributes for that role.
* @author Ethan Cohen
*/
'use strict';
// -----------------------------------------------------------------------------

@@ -18,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -31,9 +30,13 @@

const errorMessage = role => ({
message: `Elements with the ARIA role "${role}" must have the following ` +
`attributes defined: ${validRoleTypes[role.toUpperCase()].requiredProps.toString().toLowerCase()}`,
type: 'JSXAttribute'
});
const errorMessage = role => {
const requiredProps = validRoleTypes[role.toUpperCase()].requiredProps.toString().toLowerCase();
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.

@@ -46,3 +49,3 @@ const basicValidityTests = Object.keys(validRoleTypes).map(role => {

code: `<div role="${role.toLowerCase()}" ${propChain} />`,
parserOptions
parserOptions,
};

@@ -60,4 +63,7 @@ });

{ code: '<div role="tabpanel row" />', parserOptions },
{ code: '<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>', parserOptions },
{ code: '<Bar baz />', parserOptions }
{
code: '<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>',
parserOptions,
},
{ code: '<Bar baz />', parserOptions },
].concat(basicValidityTests),

@@ -67,37 +73,93 @@

// SLIDER
{ code: '<div role="slider" />', errors: [ errorMessage('slider') ], parserOptions },
{ code: '<div role="slider" aria-valuemax />', errors: [ errorMessage('slider') ], parserOptions },
{ code: '<div role="slider" aria-valuemax aria-valuemin />', errors: [ errorMessage('slider') ], parserOptions },
{ code: '<div role="slider" aria-valuemax aria-valuenow />', errors: [ errorMessage('slider') ], parserOptions },
{ code: '<div role="slider" aria-valuemin aria-valuenow />', errors: [ errorMessage('slider') ], parserOptions },
{ code: '<div role="slider" />', errors: [errorMessage('slider')], parserOptions },
{
code: '<div role="slider" aria-valuemax />',
errors: [errorMessage('slider')],
parserOptions,
},
{
code: '<div role="slider" aria-valuemax aria-valuemin />',
errors: [errorMessage('slider')],
parserOptions,
},
{
code: '<div role="slider" aria-valuemax aria-valuenow />',
errors: [errorMessage('slider')],
parserOptions,
},
{
code: '<div role="slider" aria-valuemin aria-valuenow />',
errors: [errorMessage('slider')],
parserOptions,
},
// SPINBUTTON
{ code: '<div role="spinbutton" />', errors: [ errorMessage('spinbutton') ], parserOptions },
{ code: '<div role="spinbutton" aria-valuemax />', errors: [ errorMessage('spinbutton') ], parserOptions },
{ code: '<div role="spinbutton" aria-valuemax aria-valuemin />', errors: [ errorMessage('spinbutton') ], parserOptions },
{ code: '<div role="spinbutton" aria-valuemax aria-valuenow />', errors: [ errorMessage('spinbutton') ], parserOptions },
{ code: '<div role="spinbutton" aria-valuemin aria-valuenow />', errors: [ errorMessage('spinbutton') ], parserOptions },
{ code: '<div role="spinbutton" />', errors: [errorMessage('spinbutton')], parserOptions },
{
code: '<div role="spinbutton" aria-valuemax />',
errors: [errorMessage('spinbutton')],
parserOptions,
},
{
code: '<div role="spinbutton" aria-valuemax aria-valuemin />',
errors: [errorMessage('spinbutton')],
parserOptions,
},
{
code: '<div role="spinbutton" aria-valuemax aria-valuenow />',
errors: [errorMessage('spinbutton')],
parserOptions,
},
{
code: '<div role="spinbutton" aria-valuemin aria-valuenow />',
errors: [errorMessage('spinbutton')],
parserOptions,
},
// CHECKBOX
{ code: '<div role="checkbox" />', errors: [ errorMessage('checkbox') ], parserOptions },
{ code: '<div role="checkbox" checked />', errors: [ errorMessage('checkbox') ], parserOptions },
{ code: '<div role="checkbox" aria-chcked />', errors: [ errorMessage('checkbox') ], parserOptions },
{ code: '<div role="checkbox" />', errors: [errorMessage('checkbox')], parserOptions },
{ code: '<div role="checkbox" checked />', errors: [errorMessage('checkbox')], parserOptions },
{
code: '<div role="checkbox" aria-chcked />',
errors: [errorMessage('checkbox')],
parserOptions,
},
{
code: '<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>',
errors: [ errorMessage('checkbox') ],
parserOptions
errors: [errorMessage('checkbox')],
parserOptions,
},
// COMBOBOX
{ code: '<div role="combobox" />', errors: [ errorMessage('combobox') ], parserOptions },
{ code: '<div role="combobox" expanded />', errors: [ errorMessage('combobox') ], parserOptions },
{ code: '<div role="combobox" aria-expandd />', errors: [ errorMessage('combobox') ], parserOptions },
{ code: '<div role="combobox" />', errors: [errorMessage('combobox')], parserOptions },
{ code: '<div role="combobox" expanded />', errors: [errorMessage('combobox')], parserOptions },
{
code: '<div role="combobox" aria-expandd />',
errors: [errorMessage('combobox')],
parserOptions,
},
// SCROLLBAR
{ code: '<div role="scrollbar" />', errors: [ errorMessage('scrollbar') ], parserOptions },
{ code: '<div role="scrollbar" aria-valuemax />', errors: [ errorMessage('scrollbar') ], parserOptions },
{ code: '<div role="scrollbar" aria-valuemax aria-valuemin />', errors: [ errorMessage('scrollbar') ], parserOptions },
{ code: '<div role="scrollbar" aria-valuemax aria-valuenow />', errors: [ errorMessage('scrollbar') ], parserOptions },
{ code: '<div role="scrollbar" aria-valuemin aria-valuenow />', errors: [ errorMessage('scrollbar') ], parserOptions }
]
{ code: '<div role="scrollbar" />', errors: [errorMessage('scrollbar')], parserOptions },
{
code: '<div role="scrollbar" aria-valuemax />',
errors: [errorMessage('scrollbar')],
parserOptions,
},
{
code: '<div role="scrollbar" aria-valuemax aria-valuemin />',
errors: [errorMessage('scrollbar')],
parserOptions,
},
{
code: '<div role="scrollbar" aria-valuemax aria-valuenow />',
errors: [errorMessage('scrollbar')],
parserOptions,
},
{
code: '<div role="scrollbar" aria-valuemin aria-valuenow />',
errors: [errorMessage('scrollbar')],
parserOptions,
},
],
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -15,2 +13,4 @@ // Requirements

import { RuleTester } from 'eslint';
import ROLES from '../../../src/util/attributes/role';
import ARIA from '../../../src/util/attributes/ARIA';

@@ -20,4 +20,4 @@ const parserOptions = {

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -33,3 +33,4 @@

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}. \
This role is implicit on the element ${tag}.`;
}

@@ -42,8 +43,5 @@

message: generateErrorMessage(attr, role, tag, isImplicit),
type: 'JSXOpeningElement'
type: 'JSXOpeningElement',
});
import ROLES from '../../../src/util/attributes/role';
import ARIA from '../../../src/util/attributes/ARIA';
const nonAbstractRoles = Object.keys(ROLES).filter(role => ROLES[role].abstract === false);

@@ -53,22 +51,24 @@

const validPropsForRole = ROLES[role.toUpperCase()].props;
const invalidPropsForRole = Object.keys(ARIA).filter(attribute => validPropsForRole.indexOf(attribute) === -1);
const invalidPropsForRole = Object.keys(ARIA)
.filter(attribute => validPropsForRole.indexOf(attribute) === -1);
const normalRole = role.toLowerCase();
tests[0] = tests[0].concat(validPropsForRole.map(prop => ({
const allTests = [];
allTests[0] = tests[0].concat(validPropsForRole.map(prop => ({
code: `<div role="${normalRole}" ${prop.toLowerCase()} />`,
parserOptions
parserOptions,
})));
tests[1] = tests[1].concat(invalidPropsForRole.map(prop => ({
allTests[1] = tests[1].concat(invalidPropsForRole.map(prop => ({
code: `<div role="${normalRole}" ${prop.toLowerCase()} />`,
parserOptions,
errors: [ errorMessage(prop.toLowerCase(), normalRole, 'div', false) ]
errors: [errorMessage(prop.toLowerCase(), normalRole, 'div', false)],
})));
return tests;
return allTests;
}, [[], []]);
}, [ [], [] ]);
const [validTests, invalidTests] = createTests(nonAbstractRoles);
const [ validTests, invalidTests ] = createTests(nonAbstractRoles);
ruleTester.run('role-supports-aria-props', rule, {

@@ -79,2 +79,3 @@ valid: [

{ code: '<div id="main" />', parserOptions },
{ code: '<div role="presentation" {...props} />', parserOptions },
{ code: '<Foo.Bar baz={true} />', parserOptions },

@@ -150,18 +151,18 @@

// IMG TESTS - implicit role is `presentation`
{ code: '<img alt="" aria-atomic />', parserOptions },
{ code: '<img alt="" aria-busy />', parserOptions },
{ code: '<img alt="" aria-controls />', parserOptions },
{ code: '<img alt="" aria-describedby />', parserOptions },
{ code: '<img alt="" aria-disabled />', parserOptions },
{ code: '<img alt="" aria-dropeffect />', parserOptions },
{ code: '<img alt="" aria-flowto />', parserOptions },
{ code: '<img alt="" aria-grabbed />', parserOptions },
{ code: '<img alt="" aria-haspopup />', parserOptions },
{ code: '<img alt="" aria-hidden />', parserOptions },
{ code: '<img alt="" aria-invalid />', parserOptions },
{ code: '<img alt="" aria-label />', parserOptions },
{ code: '<img alt="" aria-labelledby />', parserOptions },
{ code: '<img alt="" aria-live />', parserOptions },
{ code: '<img alt="" aria-owns />', parserOptions },
{ code: '<img alt="" aria-relevant />', parserOptions },
{ code: '<img alt="" aria-atomic />', parserOptions },
{ code: '<img alt="" aria-busy />', parserOptions },
{ code: '<img alt="" aria-controls />', parserOptions },
{ code: '<img alt="" aria-describedby />', parserOptions },
{ code: '<img alt="" aria-disabled />', parserOptions },
{ code: '<img alt="" aria-dropeffect />', parserOptions },
{ code: '<img alt="" aria-flowto />', parserOptions },
{ code: '<img alt="" aria-grabbed />', parserOptions },
{ code: '<img alt="" aria-haspopup />', parserOptions },
{ code: '<img alt="" aria-hidden />', parserOptions },
{ code: '<img alt="" aria-invalid />', parserOptions },
{ code: '<img alt="" aria-label />', parserOptions },
{ code: '<img alt="" aria-labelledby />', parserOptions },
{ code: '<img alt="" aria-live />', parserOptions },
{ code: '<img alt="" aria-owns />', parserOptions },
{ code: '<img alt="" aria-relevant />', parserOptions },

@@ -433,3 +434,3 @@ // this will have role of `img`

{ code: '<thead aria-expanded />', parserOptions },
{ code: '<ul aria-expanded />', parserOptions }
{ code: '<ul aria-expanded />', parserOptions },

@@ -442,31 +443,31 @@ ].concat(validTests),

code: '<a href="#" aria-checked />',
errors: [ errorMessage('aria-checked', 'link', 'a', true) ],
parserOptions
errors: [errorMessage('aria-checked', 'link', 'a', true)],
parserOptions,
},
{
code: '<area href="#" aria-checked />',
errors: [ errorMessage('aria-checked', 'link', 'area', true) ],
parserOptions
errors: [errorMessage('aria-checked', 'link', 'area', true)],
parserOptions,
},
{
code: '<link href="#" aria-checked />',
errors: [ errorMessage('aria-checked', 'link', 'link', true) ],
parserOptions
errors: [errorMessage('aria-checked', 'link', 'link', true)],
parserOptions,
},
{
code: '<img alt="" aria-checked />',
errors: [ errorMessage('aria-checked', 'presentation', 'img', true) ],
parserOptions
errors: [errorMessage('aria-checked', 'presentation', 'img', true)],
parserOptions,
},
{
code: '<menu type="toolbar" aria-checked />',
errors: [ errorMessage('aria-checked', 'toolbar', 'menu', true) ],
parserOptions
errors: [errorMessage('aria-checked', 'toolbar', 'menu', true)],
parserOptions,
},
{
code: '<aside aria-checked />',
errors: [ errorMessage('aria-checked', 'complementary', 'aside', true) ],
parserOptions
}
].concat(invalidTests)
errors: [errorMessage('aria-checked', 'complementary', 'aside', true)],
parserOptions,
},
].concat(invalidTests),
});

@@ -6,4 +6,2 @@ /**

'use strict';
// -----------------------------------------------------------------------------

@@ -19,4 +17,4 @@ // Requirements

ecmaFeatures: {
jsx: true
}
jsx: true,
},
};

@@ -32,3 +30,3 @@

message: 'Avoid positive integer values for tabIndex.',
type: 'JSXAttribute'
type: 'JSXAttribute',
};

@@ -55,12 +53,12 @@

{ code: '<div tabIndex={-5.5} />', parserOptions },
{ code: '<div tabIndex={-5} />', parserOptions }
{ code: '<div tabIndex={-5} />', parserOptions },
],
invalid: [
{ code: '<div tabIndex="1" />', errors: [ expectedError ], parserOptions },
{ code: '<div tabIndex={1} />', errors: [ expectedError ], parserOptions },
{ code: '<div tabIndex={"1"} />', errors: [ expectedError ], parserOptions },
{ code: '<div tabIndex={`1`} />', errors: [ expectedError ], parserOptions },
{ code: '<div tabIndex={1.589} />', errors: [ expectedError ], parserOptions }
]
{ code: '<div tabIndex="1" />', errors: [expectedError], parserOptions },
{ code: '<div tabIndex={1} />', errors: [expectedError], parserOptions },
{ code: '<div tabIndex={"1"} />', errors: [expectedError], parserOptions },
{ code: '<div tabIndex={`1`} />', errors: [expectedError], parserOptions },
{ code: '<div tabIndex={1.589} />', errors: [expectedError], parserOptions },
],
});
/* eslint-env mocha */
'use strict';
import assert from 'assert';

@@ -26,4 +24,4 @@ import getSuggestion from '../../../src/util/getSuggestion';

const word = 'fo';
const dictionary = [ 'foo', 'bar', 'baz' ];
const expected = [ 'foo' ];
const dictionary = ['foo', 'bar', 'baz'];
const expected = ['foo'];
const actual = getSuggestion(word, dictionary);

@@ -36,4 +34,4 @@

const word = 'theer';
const dictionary = [ 'there', 'their', 'foo', 'bar' ];
const expected = [ 'their', 'there' ];
const dictionary = ['there', 'their', 'foo', 'bar'];
const expected = ['their', 'there'];
const actual = getSuggestion(word, dictionary);

@@ -44,7 +42,8 @@

it('should return one correct suggestion given real word and a dictionary and a limit of 1', () => {
it('should return correct # of suggestions given the limit argument', () => {
const word = 'theer';
const dictionary = [ 'there', 'their', 'foo', 'bar' ];
const expected = [ 'their' ];
const actual = getSuggestion(word, dictionary, 1);
const dictionary = ['there', 'their', 'foo', 'bar'];
const limit = 1;
const expected = 1;
const actual = getSuggestion(word, dictionary, limit).length;

@@ -51,0 +50,0 @@ assert.deepEqual(expected, actual);

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc