-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
build(deps): bump eslint-plugin-jsx-a11y from 6.9.0 to 6.10.0 #1348
Merged
github-actions
merged 1 commit into
main
from
dependabot/npm_and_yarn/eslint-plugin-jsx-a11y-6.10.0
Oct 1, 2024
Merged
build(deps): bump eslint-plugin-jsx-a11y from 6.9.0 to 6.10.0 #1348
github-actions
merged 1 commit into
main
from
dependabot/npm_and_yarn/eslint-plugin-jsx-a11y-6.10.0
Oct 1, 2024
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Bumps [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) from 6.9.0 to 6.10.0. - [Release notes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/CHANGELOG.md) - [Commits](jsx-eslint/eslint-plugin-jsx-a11y@v6.9.0...v6.10.0) --- updated-dependencies: - dependency-name: eslint-plugin-jsx-a11y dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
dependabot
bot
added
dependencies
Pull requests that update a dependency file
javascript
Pull requests that update Javascript code
labels
Oct 1, 2024
Diff between eslint-plugin-jsx-a11y 6.9.0 and 6.10.0diff --git a/__tests__/src/rules/anchor-ambiguous-text-test.js b/__tests__/src/rules/anchor-ambiguous-text-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/anchor-ambiguous-text-test.js
+++ b/__tests__/src/rules/anchor-ambiguous-text-test.js
@@ -1,3 +1,2 @@
-/* eslint-env jest */
/**
* @fileoverview Enforce `<a>` text to not exactly match "click here", "here", "link", or "a link".
diff --git a/__tests__/src/rules/anchor-is-valid-test.js b/__tests__/src/rules/anchor-is-valid-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/anchor-is-valid-test.js
+++ b/__tests__/src/rules/anchor-is-valid-test.js
@@ -273,12 +273,7 @@
},
- // CUSTOM COMPONENTS AND SPECIALLINK AND ASPECT
+ // CUSTOM COMPONENTS AND SPECIAL LINK AND ASPECT
{ code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
{ code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
- { code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
- { code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
- { code: '<Anchor hrefLeft={undefined} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
- { code: '<Anchor hrefLeft={null} />', options: componentsAndSpecialLinkAndInvalidHrefAspect },
-
)).map(parserOptionsMapper),
invalid: parsers.all([].concat(
@@ -373,5 +368,5 @@
},
- // CUSTOM BOTH COMPONENTS AND SPECIALLINK TESTS
+ // CUSTOM BOTH COMPONENTS AND SPECIAL LINK TESTS
// NO HREF
{ code: '<Anchor Anchor={undefined} />', errors: [noHrefexpectedError], options: componentsAndSpecialLink },
@@ -523,5 +518,5 @@
},
- // CUSTOM COMPONENTS AND SPECIALLINK AND ASPECT
+ // CUSTOM COMPONENTS AND SPECIAL LINK AND ASPECT
{
code: '<Anchor hrefLeft={undefined} />',
@@ -534,24 +529,4 @@
errors: [noHrefexpectedError],
},
- {
- code: '<Anchor hrefLeft={undefined} />',
- options: componentsAndSpecialLinkAndNoHrefAspect,
- errors: [noHrefexpectedError],
- },
- {
- code: '<Anchor hrefLeft={null} />',
- options: componentsAndSpecialLinkAndNoHrefAspect,
- errors: [noHrefexpectedError],
- },
- {
- code: '<Anchor hrefLeft={undefined} />',
- options: componentsAndSpecialLinkAndNoHrefAspect,
- errors: [noHrefexpectedError],
- },
- {
- code: '<Anchor hrefLeft={null} />',
- options: componentsAndSpecialLinkAndNoHrefAspect,
- errors: [noHrefexpectedError],
- },
)).map(parserOptionsMapper),
});
diff --git a/__tests__/src/rules/aria-proptypes-test.js b/__tests__/src/rules/aria-proptypes-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/aria-proptypes-test.js
+++ b/__tests__/src/rules/aria-proptypes-test.js
@@ -10,5 +10,6 @@
import { aria } from 'aria-query';
import { RuleTester } from 'eslint';
-import expect from 'expect';
+import test from 'tape';
+
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
import parsers from '../../__util__/helpers/parsers';
@@ -52,11 +53,12 @@
};
-describe('validityCheck', () => {
- it('should false for an unknown expected type', () => {
- expect(validityCheck(
- null,
- null,
- )).toBe(false);
- });
+test('validityCheck', (t) => {
+ t.equal(
+ validityCheck(null, null),
+ false,
+ 'is false for an unknown expected type',
+ );
+
+ t.end();
});
diff --git a/lib/rules/aria-proptypes.js b/lib/rules/aria-proptypes.js
index v6.9.0..v6.10.0 100644
--- a/lib/rules/aria-proptypes.js
+++ b/lib/rules/aria-proptypes.js
@@ -37,5 +37,5 @@
}
};
-var validityCheck = function validityCheck(value, expectedType, permittedValues) {
+var _validityCheck = function validityCheck(value, expectedType, permittedValues) {
switch (expectedType) {
case 'boolean':
@@ -55,5 +55,5 @@
case 'idlist':
return typeof value === 'string' && value.split(' ').every(function (token) {
- return validityCheck(token, 'id', []);
+ return _validityCheck(token, 'id', []);
});
case 'tokenlist':
@@ -67,5 +67,5 @@
var schema = (0, _schemas.generateObjSchema)();
var _default = exports["default"] = {
- validityCheck,
+ validityCheck: _validityCheck,
meta: {
docs: {
@@ -100,5 +100,5 @@
var allowUndefined = attributes.allowUndefined || false;
var permittedValues = attributes.values || [];
- var isValid = validityCheck(value, permittedType, permittedValues) || allowUndefined && value === undefined;
+ var isValid = _validityCheck(value, permittedType, permittedValues) || allowUndefined && value === undefined;
if (isValid) {
return;
diff --git a/__tests__/src/util/attributesComparator-test.js b/__tests__/src/util/attributesComparator-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/attributesComparator-test.js
+++ b/__tests__/src/util/attributesComparator-test.js
@@ -1,115 +1,91 @@
-import expect from 'expect';
+import test from 'tape';
+
import attributesComparator from '../../../src/util/attributesComparator';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
-describe('attributesComparator', () => {
- describe('base attributes', () => {
- let baseAttributes;
- let attributes;
- describe('are undefined', () => {
- describe('and attributes are undefined', () => {
- it('should return true', () => {
- expect(attributesComparator()).toBe(true);
- });
- });
- });
- describe('are empty', () => {
- beforeEach(() => {
- baseAttributes = [];
- });
- describe('and attributes', () => {
- describe('are empty', () => {
- attributes = [];
- it('should return true', () => {
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(true);
- });
- });
- describe('have values', () => {
- attributes = [
- JSXAttributeMock('foo', 0),
- JSXAttributeMock('bar', 'baz'),
- ];
- it('should return true', () => {
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(true);
- });
- });
- });
- });
- describe('have values', () => {
- beforeEach(() => {
- baseAttributes = [
- {
- name: 'biz',
- value: 1,
- }, {
- name: 'fizz',
- value: 'pop',
- }, {
- name: 'fuzz',
- value: 'lolz',
- },
- ];
- });
- describe('and attributes', () => {
- describe('are empty', () => {
- attributes = [];
- it('should return false', () => {
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(false);
- });
- });
- describe('have values', () => {
- describe('and the values are the different', () => {
- it('should return false', () => {
- attributes = [
- JSXElementMock(),
- JSXAttributeMock('biz', 2),
- JSXAttributeMock('ziff', 'opo'),
- JSXAttributeMock('far', 'lolz'),
- ];
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(false);
- });
- });
- describe('and the values are a subset', () => {
- it('should return true', () => {
- attributes = [
- JSXAttributeMock('biz', 1),
- JSXAttributeMock('fizz', 'pop'),
- JSXAttributeMock('goo', 'gazz'),
- ];
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(false);
- });
- });
- describe('and the values are the same', () => {
- it('should return true', () => {
- attributes = [
- JSXAttributeMock('biz', 1),
- JSXAttributeMock('fizz', 'pop'),
- JSXAttributeMock('fuzz', 'lolz'),
- ];
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(true);
- });
- });
- describe('and the values are a superset', () => {
- it('should return true', () => {
- attributes = [
- JSXAttributeMock('biz', 1),
- JSXAttributeMock('fizz', 'pop'),
- JSXAttributeMock('fuzz', 'lolz'),
- JSXAttributeMock('dar', 'tee'),
- ];
- expect(attributesComparator(baseAttributes, attributes))
- .toBe(true);
- });
- });
- });
- });
- });
- });
+test('attributesComparator', (t) => {
+ t.equal(
+ attributesComparator(),
+ true,
+ 'baseAttributes are undefined and attributes are undefined -> true',
+ );
+
+ t.equal(
+ attributesComparator([], []),
+ true,
+ 'baseAttributes are empty and attributes are empty -> true',
+ );
+
+ t.equal(
+ attributesComparator([], [
+ JSXAttributeMock('foo', 0),
+ JSXAttributeMock('bar', 'baz'),
+ ]),
+ true,
+ 'baseAttributes are empty and attributes have values -> true',
+ );
+
+ const baseAttributes = [
+ {
+ name: 'biz',
+ value: 1,
+ }, {
+ name: 'fizz',
+ value: 'pop',
+ }, {
+ name: 'fuzz',
+ value: 'lolz',
+ },
+ ];
+
+ t.equal(
+ attributesComparator(baseAttributes, []),
+ false,
+ 'baseAttributes have values and attributes are empty -> false',
+ );
+
+ t.equal(
+ attributesComparator(baseAttributes, [
+ JSXElementMock(),
+ JSXAttributeMock('biz', 2),
+ JSXAttributeMock('ziff', 'opo'),
+ JSXAttributeMock('far', 'lolz'),
+ ]),
+ false,
+ 'baseAttributes have values and attributes have values, and the values are different -> false',
+ );
+
+ t.equal(
+ attributesComparator(baseAttributes, [
+ JSXAttributeMock('biz', 1),
+ JSXAttributeMock('fizz', 'pop'),
+ JSXAttributeMock('goo', 'gazz'),
+ ]),
+ false,
+ 'baseAttributes have values and attributes have values, and the values are a subset -> false',
+ );
+
+ t.equal(
+ attributesComparator(baseAttributes, [
+ JSXAttributeMock('biz', 1),
+ JSXAttributeMock('fizz', 'pop'),
+ JSXAttributeMock('fuzz', 'lolz'),
+ ]),
+ true,
+ 'baseAttributes have values and attributes have values, and the values are the same -> true',
+ );
+
+ t.equal(
+ attributesComparator(baseAttributes, [
+ JSXAttributeMock('biz', 1),
+ JSXAttributeMock('fizz', 'pop'),
+ JSXAttributeMock('fuzz', 'lolz'),
+ JSXAttributeMock('dar', 'tee'),
+ ]),
+ true,
+ 'baseAttributes have values and attributes have values, and the values are a superset -> true',
+ );
+
+ t.end();
});
diff --git a/lib/rules/control-has-associated-label.js b/lib/rules/control-has-associated-label.js
index v6.9.0..v6.10.0 100644
--- a/lib/rules/control-has-associated-label.js
+++ b/lib/rules/control-has-associated-label.js
@@ -85,5 +85,5 @@
// Prevent crazy recursion.
var recursionDepth = Math.min(options.depth === undefined ? 2 : options.depth, 25);
- hasAccessibleLabel = (0, _mayHaveAccessibleLabel["default"])(node, recursionDepth, labelAttributes);
+ hasAccessibleLabel = (0, _mayHaveAccessibleLabel["default"])(node, recursionDepth, labelAttributes, elementType, controlComponents);
}
if (!hasAccessibleLabel) {
diff --git a/__tests__/src/util/getAccessibleChildText-test.js b/__tests__/src/util/getAccessibleChildText-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getAccessibleChildText-test.js
+++ b/__tests__/src/util/getAccessibleChildText-test.js
@@ -1,41 +1,50 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import getAccessibleChildText from '../../../src/util/getAccessibleChildText';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
-describe('getAccessibleChildText', () => {
- it('returns the aria-label when present', () => {
- expect(getAccessibleChildText(JSXElementMock(
+test('getAccessibleChildText', (t) => {
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-label', 'foo')],
- ), elementType)).toBe('foo');
- });
+ ), elementType),
+ 'foo',
+ 'returns the aria-label when present',
+ );
- it('returns the aria-label instead of children', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-label', 'foo')],
[{ type: 'JSXText', value: 'bar' }],
- ), elementType)).toBe('foo');
- });
+ ), elementType),
+ 'foo',
+ 'returns the aria-label instead of children',
+ );
- it('skips elements with aria-hidden=true', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-hidden', 'true')],
- ), elementType)).toBe('');
- });
+ ), elementType),
+ '',
+ 'skips elements with aria-hidden=true',
+ );
- it('returns literal value for JSXText child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'JSXText', value: 'bar' }],
- ), elementType)).toBe('bar');
- });
+ ), elementType),
+ 'bar',
+ 'returns literal value for JSXText child',
+ );
- it('returns alt text for img child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
@@ -44,9 +53,11 @@
JSXAttributeMock('alt', 'a sensible label'),
])],
- ), elementType)).toBe('a sensible label');
- });
+ ), elementType),
+ 'a sensible label',
+ 'returns alt text for img child',
+ );
- it('returns blank when alt tag is used on arbitrary element', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
@@ -54,41 +65,51 @@
JSXAttributeMock('alt', 'a sensible label'),
])],
- ), elementType)).toBe('');
- });
+ ), elementType),
+ '',
+ 'returns blank when alt tag is used on arbitrary element',
+ );
- it('returns literal value for JSXText child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'bar' }],
- ), elementType)).toBe('bar');
- });
+ ), elementType),
+ 'bar',
+ 'returns literal value for JSXText child',
+ );
- it('returns trimmed literal value for JSXText child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: ' bar ' }],
- ), elementType)).toBe('bar');
- });
+ ), elementType),
+ 'bar',
+ 'returns trimmed literal value for JSXText child',
+ );
- it('returns space-collapsed literal value for JSXText child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo bar' }],
- ), elementType)).toBe('foo bar');
- });
+ ), elementType),
+ 'foo bar',
+ 'returns space-collapsed literal value for JSXText child',
+ );
- it('returns punctuation-stripped literal value for JSXText child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo, bar. baz? foo; bar:' }],
- ), elementType)).toBe('foo bar baz foo bar');
- });
+ ), elementType),
+ 'foo bar baz foo bar',
+ 'returns punctuation-stripped literal value for JSXText child',
+ );
- it('returns recursive value for JSXElement child', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
@@ -98,9 +119,11 @@
[{ type: 'Literal', value: 'bar' }],
)],
- ), elementType)).toBe('bar');
- });
+ ), elementType),
+ 'bar',
+ 'returns recursive value for JSXElement child',
+ );
- it('skips children with aria-hidden-true', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
@@ -113,29 +136,39 @@
)],
)],
- ), elementType)).toBe('');
- });
+ ), elementType),
+ '',
+ 'skips children with aria-hidden-true',
+ );
- it('joins multiple children properly - no spacing', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo' }, { type: 'Literal', value: 'bar' }],
- ), elementType)).toBe('foo bar');
- });
+ ), elementType),
+ 'foo bar',
+ 'joins multiple children properly - no spacing',
+ );
- it('joins multiple children properly - with spacing', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: ' foo ' }, { type: 'Literal', value: ' bar ' }],
- ), elementType)).toBe('foo bar');
- });
+ ), elementType),
+ 'foo bar',
+ 'joins multiple children properly - with spacing',
+ );
- it('skips unknown elements', () => {
- expect(getAccessibleChildText(JSXElementMock(
+ t.equal(
+ getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo' }, { type: 'Unknown' }, { type: 'Literal', value: 'bar' }],
- ), elementType)).toBe('foo bar');
- });
+ ), elementType),
+ 'foo bar',
+ 'skips unknown elements',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/getComputedRole-test.js b/__tests__/src/util/getComputedRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getComputedRole-test.js
+++ b/__tests__/src/util/getComputedRole-test.js
@@ -1,71 +1,71 @@
-import expect from 'expect';
+import test from 'tape';
+
import getComputedRole from '../../../src/util/getComputedRole';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('getComputedRole', () => {
- describe('explicit role', () => {
- describe('valid role', () => {
- it('should return the role', () => {
- expect(getComputedRole(
- 'div',
- [JSXAttributeMock('role', 'button')],
- )).toBe('button');
- });
- });
- describe('invalid role', () => {
- describe('has implicit', () => {
- it('should return the implicit role', () => {
- expect(getComputedRole(
- 'li',
- [JSXAttributeMock('role', 'beeswax')],
- )).toBe('listitem');
- });
- });
- describe('lacks implicit', () => {
- it('should return null', () => {
- expect(getComputedRole(
- 'div',
- [JSXAttributeMock('role', 'beeswax')],
- )).toBeNull();
- });
- });
- });
+test('getComputedRole', (t) => {
+ t.equal(
+ getComputedRole(
+ 'div',
+ [JSXAttributeMock('role', 'button')],
+ ),
+ 'button',
+ 'explicit role + valid role -> returns the role',
+ );
- describe('no role', () => {
- describe('has implicit', () => {
- it('should return the implicit role', () => {
- expect(getComputedRole(
- 'li',
- [],
- )).toBe('listitem');
- });
- });
- describe('lacks implicit', () => {
- it('should return null', () => {
- expect(getComputedRole(
- 'div',
- [],
- )).toBeNull();
- });
- });
- });
- });
- describe('implicit role', () => {
- describe('has implicit', () => {
- it('should return the implicit role', () => {
- expect(getComputedRole(
- 'li',
- [JSXAttributeMock('role', 'beeswax')],
- )).toBe('listitem');
- });
- });
- describe('lacks implicit', () => {
- it('should return null', () => {
- expect(getComputedRole(
- 'div',
- [],
- )).toBeNull();
- });
- });
- });
+ t.equal(
+ getComputedRole(
+ 'li',
+ [JSXAttributeMock('role', 'beeswax')],
+ ),
+ 'listitem',
+ 'explicit role + invalid role + has implicit -> returns the implicit role',
+ );
+
+ t.equal(
+ getComputedRole(
+ 'div',
+ [JSXAttributeMock('role', 'beeswax')],
+ ),
+ null,
+ 'explicit role + invalid role + lacks implicit -> returns null',
+ );
+
+ t.equal(
+ getComputedRole(
+ 'li',
+ [],
+ ),
+ 'listitem',
+ 'explicit role + no role + has implicit -> returns the implicit role',
+ );
+
+ t.equal(
+ getComputedRole(
+ 'div',
+ [],
+ ),
+ null,
+ 'explicit role + no role + lacks implicit -> returns null',
+ );
+
+ t.equal(
+ getComputedRole(
+ 'li',
+ [JSXAttributeMock('role', 'beeswax')],
+ ),
+ 'listitem',
+ 'implicit role + has implicit -> returns the implicit role',
+ );
+
+ t.equal(
+ getComputedRole(
+ 'div',
+ [],
+ ),
+ null,
+ 'implicit role + lacks implicit -> returns null',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/getElementType-test.js b/__tests__/src/util/getElementType-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getElementType-test.js
+++ b/__tests__/src/util/getElementType-test.js
@@ -1,29 +1,40 @@
-import expect from 'expect';
+import test from 'tape';
+
import getElementType from '../../../src/util/getElementType';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('getElementType', () => {
- describe('no settings in context', () => {
+test('getElementType', (t) => {
+ t.test('no settings in context', (st) => {
const elementType = getElementType({ settings: {} });
- it('should return the exact tag name for a DOM element', () => {
- expect(elementType(JSXElementMock('input').openingElement)).toBe('input');
- });
+ st.equal(
+ elementType(JSXElementMock('input').openingElement),
+ 'input',
+ 'returns the exact tag name for a DOM element',
+ );
- it('should return the exact tag name for a custom element', () => {
- expect(elementType(JSXElementMock('CustomInput').openingElement)).toBe('CustomInput');
- });
+ st.equal(
+ elementType(JSXElementMock('CustomInput').openingElement),
+ 'CustomInput',
+ 'returns the exact tag name for a custom element',
+ );
- it('should return the exact tag name for names that are in Object.prototype', () => {
- expect(elementType(JSXElementMock('toString').openingElement)).toBe('toString');
- });
+ st.equal(
+ elementType(JSXElementMock('toString').openingElement),
+ 'toString',
+ 'returns the exact tag name for names that are in Object.prototype',
+ );
- it('should return the default tag name provided', () => {
- expect(elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement)).toBe('span');
- });
+ st.equal(
+ elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
+ 'span',
+ 'returns the default tag name provided',
+ );
+
+ st.end();
});
- describe('components settings in context', () => {
+ t.test('components settings in context', (st) => {
const elementType = getElementType({
settings: {
@@ -36,22 +47,32 @@
});
- it('should return the exact tag name for a DOM element', () => {
- expect(elementType(JSXElementMock('input').openingElement)).toBe('input');
- });
+ st.equal(
+ elementType(JSXElementMock('input').openingElement),
+ 'input',
+ 'returns the exact tag name for a DOM element',
+ );
- it('should return the mapped tag name for a custom element', () => {
- expect(elementType(JSXElementMock('CustomInput').openingElement)).toBe('input');
- });
+ st.equal(
+ elementType(JSXElementMock('CustomInput').openingElement),
+ 'input',
+ 'returns the mapped tag name for a custom element',
+ );
- it('should return the exact tag name for a custom element not in the components map', () => {
- expect(elementType(JSXElementMock('CityInput').openingElement)).toBe('CityInput');
- });
+ st.equal(
+ elementType(JSXElementMock('CityInput').openingElement),
+ 'CityInput',
+ 'returns the exact tag name for a custom element not in the components map',
+ );
- it('should return the default tag name since not polymorphicPropName was provided', () => {
- expect(elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement)).toBe('span');
- });
+ st.equal(
+ elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
+ 'span',
+ 'return the default tag name since not polymorphicPropName was provided',
+ );
+
+ st.end();
});
- describe('polymorphicPropName settings in context', () => {
+ t.test('polymorphicPropName settings in context', (st) => {
const elementType = getElementType({
settings: {
@@ -65,15 +86,69 @@
});
- it('should return the tag name provided by the polymorphic prop, "asChild", defined in the settings', () => {
- expect(elementType(JSXElementMock('span', [JSXAttributeMock('asChild', 'h1')]).openingElement)).toBe('h1');
- });
+ st.equal(
+ elementType(JSXElementMock('span', [JSXAttributeMock('asChild', 'h1')]).openingElement),
+ 'h1',
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings',
+ );
- it('should return the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag', () => {
- expect(elementType(JSXElementMock('CustomButton', [JSXAttributeMock('asChild', 'a')]).openingElement)).toBe('a');
+ st.equal(
+ elementType(JSXElementMock('CustomButton', [JSXAttributeMock('asChild', 'a')]).openingElement),
+ 'a',
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag',
+ );
+
+ st.equal(
+ elementType(JSXElementMock('CustomButton', [JSXAttributeMock('as', 'a')]).openingElement),
+ 'button',
+ 'returns the tag name provided by the componnet mapping if the polymorphic prop, "asChild", defined in the settings is not set',
+ );
+
+ st.end();
+ });
+
+ t.test('polymorphicPropName settings and explicitly defined polymorphicAllowList in context', (st) => {
+ const elementType = getElementType({
+ settings: {
+ 'jsx-a11y': {
+ polymorphicPropName: 'asChild',
+ polymorphicAllowList: [
+ 'Box',
+ 'Icon',
+ ],
+ components: {
+ Box: 'div',
+ Icon: 'svg',
+ },
+ },
+ },
});
- it('should return the tag name provided by the componnet mapping if the polymorphic prop, "asChild", defined in the settings is not set', () => {
- expect(elementType(JSXElementMock('CustomButton', [JSXAttributeMock('as', 'a')]).openingElement)).toBe('button');
- });
+ st.equal(
+ elementType(JSXElementMock('Spinner', [JSXAttributeMock('asChild', 'img')]).openingElement),
+ 'Spinner',
+ 'does not use the polymorphic prop if polymorphicAllowList is defined, but element is not part of polymorphicAllowList',
+ );
+
+ st.equal(
+ elementType(JSXElementMock('Icon', [JSXAttributeMock('asChild', 'img')]).openingElement),
+ 'img',
+ 'uses the polymorphic prop if it is in explicitly defined polymorphicAllowList',
+ );
+
+ st.equal(
+ elementType(JSXElementMock('Box', [JSXAttributeMock('asChild', 'span')]).openingElement),
+ 'span',
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag',
+ );
+
+ st.equal(
+ elementType(JSXElementMock('Box', [JSXAttributeMock('as', 'a')]).openingElement),
+ 'div',
+ 'returns the tag name provided by the component mapping if the polymorphic prop, "asChild", defined in the settings is not set',
+ );
+
+ st.end();
});
+
+ t.end();
});
diff --git a/lib/util/getElementType.js b/lib/util/getElementType.js
index v6.9.0..v6.10.0 100644
--- a/lib/util/getElementType.js
+++ b/lib/util/getElementType.js
@@ -6,14 +6,19 @@
exports["default"] = void 0;
var _hasown = _interopRequireDefault(require("hasown"));
+var _arrayIncludes = _interopRequireDefault(require("array-includes"));
var _jsxAstUtils = require("jsx-ast-utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
var getElementType = function getElementType(context) {
- var _settings$jsxA11y, _settings$jsxA11y2;
+ var _settings$jsxA11y, _settings$jsxA11y2, _settings$jsxA11y3;
var settings = context.settings;
var polymorphicPropName = (_settings$jsxA11y = settings['jsx-a11y']) === null || _settings$jsxA11y === void 0 ? void 0 : _settings$jsxA11y.polymorphicPropName;
- var componentMap = (_settings$jsxA11y2 = settings['jsx-a11y']) === null || _settings$jsxA11y2 === void 0 ? void 0 : _settings$jsxA11y2.components;
+ var polymorphicAllowList = (_settings$jsxA11y2 = settings['jsx-a11y']) === null || _settings$jsxA11y2 === void 0 ? void 0 : _settings$jsxA11y2.polymorphicAllowList;
+ var componentMap = (_settings$jsxA11y3 = settings['jsx-a11y']) === null || _settings$jsxA11y3 === void 0 ? void 0 : _settings$jsxA11y3.components;
return function (node) {
var polymorphicProp = polymorphicPropName ? (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, polymorphicPropName)) : undefined;
- var rawType = polymorphicProp !== null && polymorphicProp !== void 0 ? polymorphicProp : (0, _jsxAstUtils.elementType)(node);
+ var rawType = (0, _jsxAstUtils.elementType)(node);
+ if (polymorphicProp && (!polymorphicAllowList || (0, _arrayIncludes["default"])(polymorphicAllowList, rawType))) {
+ rawType = polymorphicProp;
+ }
if (!componentMap) {
return rawType;
diff --git a/__tests__/src/util/getExplicitRole-test.js b/__tests__/src/util/getExplicitRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getExplicitRole-test.js
+++ b/__tests__/src/util/getExplicitRole-test.js
@@ -1,30 +1,35 @@
-import expect from 'expect';
+import test from 'tape';
+
import getExplicitRole from '../../../src/util/getExplicitRole';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('getExplicitRole', () => {
- describe('valid role', () => {
- it('should return the role', () => {
- expect(getExplicitRole(
- 'div',
- [JSXAttributeMock('role', 'button')],
- )).toBe('button');
- });
- });
- describe('invalid role', () => {
- it('should return null', () => {
- expect(getExplicitRole(
- 'div',
- [JSXAttributeMock('role', 'beeswax')],
- )).toBeNull();
- });
- });
- describe('no role', () => {
- it('should return null', () => {
- expect(getExplicitRole(
- 'div',
- [],
- )).toBeNull();
- });
- });
+test('getExplicitRole', (t) => {
+ t.equal(
+ getExplicitRole(
+ 'div',
+ [JSXAttributeMock('role', 'button')],
+ ),
+ 'button',
+ 'valid role returns the role',
+ );
+
+ t.equal(
+ getExplicitRole(
+ 'div',
+ [JSXAttributeMock('role', 'beeswax')],
+ ),
+ null,
+ 'invalid role returns null',
+ );
+
+ t.equal(
+ getExplicitRole(
+ 'div',
+ [],
+ ),
+ null,
+ 'no role returns null',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/getImplicitRole-test.js b/__tests__/src/util/getImplicitRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getImplicitRole-test.js
+++ b/__tests__/src/util/getImplicitRole-test.js
@@ -1,21 +1,25 @@
-import expect from 'expect';
+import test from 'tape';
+
import getImplicitRole from '../../../src/util/getImplicitRole';
-describe('getImplicitRole', () => {
- describe('has implicit', () => {
- it('should return the implicit role', () => {
- expect(getImplicitRole(
- 'li',
- [],
- )).toBe('listitem');
- });
- });
- describe('lacks implicit', () => {
- it('should return null', () => {
- expect(getImplicitRole(
- 'div',
- [],
- )).toBeNull();
- });
- });
+test('getImplicitRole', (t) => {
+ t.equal(
+ getImplicitRole(
+ 'li',
+ [],
+ ),
+ 'listitem',
+ 'has implicit, returns implicit role',
+ );
+
+ t.equal(
+ getImplicitRole(
+ 'div',
+ [],
+ ),
+ null,
+ 'lacks implicit, returns null',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/getSuggestion-test.js b/__tests__/src/util/getSuggestion-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getSuggestion-test.js
+++ b/__tests__/src/util/getSuggestion-test.js
@@ -1,48 +1,33 @@
-import expect from 'expect';
+import test from 'tape';
+
import getSuggestion from '../../../src/util/getSuggestion';
-describe('spell check suggestion API', () => {
- it('should return no suggestions given empty word and no dictionary', () => {
- const word = '';
- const expected = [];
- const actual = getSuggestion(word);
+test('spell check suggestion API', (t) => {
+ t.deepEqual([], getSuggestion('foo'), 'returns no suggestions given empty word and no dictionary');
- expect(expected).toEqual(actual);
- });
+ t.deepEqual(
+ getSuggestion('foo'),
+ [],
+ 'returns no suggestions given real word and no dictionary',
+ );
- it('should return no suggestions given real word and no dictionary', () => {
- const word = 'foo';
- const expected = [];
- const actual = getSuggestion(word);
+ t.deepEqual(
+ getSuggestion('fo', ['foo', 'bar', 'baz']),
+ ['foo'],
+ 'returns correct suggestion given real word and a dictionary',
+ );
- expect(expected).toEqual(actual);
- });
+ t.deepEqual(
+ getSuggestion('theer', ['there', 'their', 'foo', 'bar']),
+ ['there', 'their'],
+ 'returns multiple correct suggestions given real word and a dictionary',
+ );
- it('should return correct suggestion given real word and a dictionary', () => {
- const word = 'fo';
- const dictionary = ['foo', 'bar', 'baz'];
- const expected = ['foo'];
- const actual = getSuggestion(word, dictionary);
+ t.deepEqual(
+ getSuggestion('theer', ['there', 'their', 'foo', 'bar'], 1),
+ ['there'],
+ 'returns correct # of suggestions given the limit argument',
+ );
- expect(expected).toEqual(actual);
- });
-
- it('should return multiple correct suggestions given real word and a dictionary', () => {
- const word = 'theer';
- const dictionary = ['there', 'their', 'foo', 'bar'];
- const expected = ['there', 'their'];
- const actual = getSuggestion(word, dictionary);
-
- expect(expected).toEqual(actual);
- });
-
- it('should return correct # of suggestions given the limit argument', () => {
- const word = 'theer';
- const dictionary = ['there', 'their', 'foo', 'bar'];
- const limit = 1;
- const expected = 1;
- const actual = getSuggestion(word, dictionary, limit).length;
-
- expect(expected).toEqual(actual);
- });
+ t.end();
});
diff --git a/__tests__/src/util/getTabIndex-test.js b/__tests__/src/util/getTabIndex-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/getTabIndex-test.js
+++ b/__tests__/src/util/getTabIndex-test.js
@@ -1,83 +1,85 @@
-import expect from 'expect';
+import test from 'tape';
+
import getTabIndex from '../../../src/util/getTabIndex';
import IdentifierMock from '../../../__mocks__/IdentifierMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('getTabIndex', () => {
- describe('tabIndex is defined', () => {
- describe('as a number ', () => {
- describe('zero', () => {
- it('should return zero', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', 0))).toBe(0);
- });
- });
- describe('positive integer', () => {
- it('should return the integer', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', 1))).toBe(1);
- });
- });
- describe('negative integer', () => {
- it('should return the integer', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', -1))).toBe(-1);
- });
- });
- describe('float', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', 9.1))).toBeUndefined();
- });
- });
- });
- describe('as a string', () => {
- describe('empty', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', ''))).toBeUndefined();
- });
- });
- describe('which converts to a number', () => {
- it('should return an integer', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', '0'))).toBe(0);
- });
- });
- describe('which is NaN', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', '0a'))).toBeUndefined();
- });
- });
- });
- describe('as a boolean', () => {
- describe('true', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', true))).toBeUndefined();
- });
- });
- describe('false', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', false))).toBeUndefined();
- });
- });
- });
- describe('as an expression', () => {
- describe('function expression', () => {
- it('should return the correct type', () => {
- const attr = function mockFn() { return 0; };
- expect(typeof getTabIndex(JSXAttributeMock('tabIndex', attr))).toEqual('function');
- });
- });
- describe('variable expression', () => {
- it('should return the Identifier name', () => {
- const name = 'identName';
- expect(getTabIndex(JSXAttributeMock(
- 'tabIndex',
- IdentifierMock(name),
- true,
- ))).toEqual(name);
- });
- });
- });
- });
- describe('tabIndex is not defined', () => {
- it('should return undefined', () => {
- expect(getTabIndex(JSXAttributeMock('tabIndex', undefined))).toBeUndefined();
- });
- });
+test('getTabIndex', (t) => {
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', 0)),
+ 0,
+ 'tabIndex is defined as zero -> zero',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', 1)),
+ 1,
+ 'tabIndex is defined as a positive integer -> returns it',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', -1)),
+ -1,
+ 'tabIndex is defined as a negative integer -> returns it',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', '')),
+ undefined,
+ 'tabIndex is defined as an empty string -> undefined',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', 9.1)),
+ undefined,
+ 'tabIndex is defined as a float -> undefined',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', '0')),
+ 0,
+ 'tabIndex is defined as a string which converts to a number -> returns the integer',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', '0a')),
+ undefined,
+ 'tabIndex is defined as a string which is NaN -> returns undefined',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', true)),
+ undefined,
+ 'tabIndex is defined as true -> returns undefined',
+ );
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', false)),
+ undefined,
+ 'tabIndex is defined as false -> returns undefined',
+ );
+
+ t.equal(
+ typeof getTabIndex(JSXAttributeMock('tabIndex', () => 0)),
+ 'function',
+ 'tabIndex is defined as a function expression -> returns the correct type',
+ );
+
+ const name = 'identName';
+ t.equal(
+ getTabIndex(JSXAttributeMock(
+ 'tabIndex',
+ IdentifierMock(name),
+ true,
+ )),
+ name,
+ 'tabIndex is defined as a variable expression -> returns the Identifier name',
+ );
+
+ t.equal(
+ getTabIndex(JSXAttributeMock('tabIndex', undefined)),
+ undefined,
+ 'tabIndex is not defined -> returns undefined',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/hasAccessibleChild-test.js b/__tests__/src/util/hasAccessibleChild-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/hasAccessibleChild-test.js
+++ b/__tests__/src/util/hasAccessibleChild-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import hasAccessibleChild from '../../../src/util/hasAccessibleChild';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
@@ -6,102 +7,151 @@
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
-describe('hasAccessibleChild', () => {
- describe('has no children and does not set dangerouslySetInnerHTML', () => {
- it('returns false', () => {
- expect(hasAccessibleChild(JSXElementMock('div', []), elementType)).toBe(false);
- });
- });
+test('hasAccessibleChild', (t) => {
+ t.equal(
+ hasAccessibleChild(JSXElementMock('div', []), elementType),
+ false,
+ 'has no children and does not set dangerouslySetInnerHTML -> false',
+ );
- describe('has no children and sets dangerouslySetInnerHTML', () => {
- it('Returns true', () => {
- const prop = JSXAttributeMock('dangerouslySetInnerHTML', true);
- const element = JSXElementMock('div', [prop], []);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [JSXAttributeMock('dangerouslySetInnerHTML', true)], []),
+ elementType,
+ ),
+ true,
+ 'has no children and sets dangerouslySetInnerHTML -> true',
+ );
- describe('has children', () => {
- it('Returns true for a Literal child', () => {
- const child = {
- type: 'Literal',
- value: 'foo',
- };
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock(
+ 'div',
+ [],
+ [{
+ type: 'Literal',
+ value: 'foo',
+ }],
+ ),
+ elementType,
+ ),
+ true,
+ 'has children + Literal child -> true',
+ );
- it('Returns true for visible child JSXElement', () => {
- const child = JSXElementMock('div', []);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [JSXElementMock('div', [])]),
+ elementType,
+ ),
+ true,
+ 'has children + visible JSXElement child -> true',
+ );
- it('Returns true for JSXText Element', () => {
- const child = {
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [{
type: 'JSXText',
value: 'foo',
- };
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ }]),
+ elementType,
+ ),
+ true,
+ 'has children + JSText element -> true',
+ );
- it('Returns false for hidden child JSXElement', () => {
- const ariaHiddenAttr = JSXAttributeMock('aria-hidden', true);
- const child = JSXElementMock('div', [ariaHiddenAttr]);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(false);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXElementMock('div', [
+ JSXAttributeMock('aria-hidden', true),
+ ]),
+ ]),
+ elementType,
+ ),
+ false,
+ 'has children + hidden child JSXElement -> false',
+ );
- it('Returns true for defined JSXExpressionContainer', () => {
- const expression = {
- type: 'Identifier',
- name: 'foo',
- };
- const child = JSXExpressionContainerMock(expression);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXExpressionContainerMock({
+ type: 'Identifier',
+ name: 'foo',
+ }),
+ ]),
+ elementType,
+ ),
+ true,
+ 'defined JSXExpressionContainer -> true',
+ );
- it('Returns false for undefined JSXExpressionContainer', () => {
- const expression = {
- type: 'Identifier',
- name: 'undefined',
- };
- const child = JSXExpressionContainerMock(expression);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(false);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXExpressionContainerMock({
+ type: 'Identifier',
+ name: 'undefined',
+ }),
+ ]),
+ elementType,
+ ),
+ false,
+ 'has children + undefined JSXExpressionContainer -> false',
+ );
- it('Returns false for unknown child type', () => {
- const child = {
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [{
type: 'Unknown',
- };
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(false);
- });
+ }]),
+ elementType,
+ ),
+ false,
+ 'unknown child type -> false',
+ );
- it('Returns true with children passed as a prop', () => {
- const children = JSXAttributeMock('children', true);
- const element = JSXElementMock('div', [children], []);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [JSXAttributeMock('children', true)], []),
+ elementType,
+ ),
+ true,
+ 'children passed as a prop -> true',
+ );
- it('Returns false for hidden child input JSXElement', () => {
- const child = JSXElementMock('input', [JSXAttributeMock('type', 'hidden')]);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(false);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXElementMock('input', [JSXAttributeMock('type', 'hidden')]),
+ ]),
+ elementType,
+ ),
+ false,
+ 'has chidren -> hidden child input JSXElement -> false',
+ );
- it('Returns true for a custom JSXElement even if type hidden', () => {
- const child = JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, elementType)).toBe(true);
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]),
+ ]),
+ elementType,
+ ),
+ true,
+ 'has children + custom JSXElement of type hidden -> true',
+ );
- it('Returns false for a custom JSXElement mapped to input if type is hidden', () => {
- const child = JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]);
- const element = JSXElementMock('div', [], [child]);
- expect(hasAccessibleChild(element, () => 'input')).toBe(false);
- });
- });
+ t.equal(
+ hasAccessibleChild(
+ JSXElementMock('div', [], [
+ JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]),
+ ]),
+ () => 'input',
+ ),
+ false,
+ 'custom JSXElement mapped to input if type is hidden -> false',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/rules/img-redundant-alt-test.js b/__tests__/src/rules/img-redundant-alt-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/img-redundant-alt-test.js
+++ b/__tests__/src/rules/img-redundant-alt-test.js
@@ -68,6 +68,6 @@
{ code: '<img alt={imageAlt.name} />' },
semver.satisfies(eslintVersion, '>= 6') ? [
- { code: '<img alt={imageAlt?.name} />', parserOptions: { ecmaVersion: 2020 } },
- { code: '<img alt="Doing cool things" aria-hidden={foo?.bar}/>', parserOptions: { ecmaVersion: 2020 } },
+ { code: '<img alt={imageAlt?.name} />', languageOptions: { ecmaVersion: 2020 } },
+ { code: '<img alt="Doing cool things" aria-hidden={foo?.bar}/>', languageOptions: { ecmaVersion: 2020 } },
] : [],
{ code: '<img alt="Photography" />;' },
diff --git a/__tests__/index-test.js b/__tests__/index-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/index-test.js
+++ b/__tests__/index-test.js
@@ -3,5 +3,6 @@
import fs from 'fs';
import path from 'path';
-import expect from 'expect';
+import test from 'tape';
+
import plugin from '../src';
@@ -9,29 +10,31 @@
.map((f) => path.basename(f, '.js'));
-describe('all rule files should be exported by the plugin', () => {
+test('all rule files should be exported by the plugin', (t) => {
rules.forEach((ruleName) => {
- it(`should export ${ruleName}`, () => {
- expect(plugin.rules[ruleName]).toEqual(
- require(path.join('../src/rules', ruleName)) // eslint-disable-line
- );
- });
+ t.equal(
+ plugin.rules[ruleName],
+ require(path.join('../src/rules', ruleName)), // eslint-disable-line import/no-dynamic-require
+ `exports ${ruleName}`,
+ );
});
+
+ t.end();
});
-describe('configurations', () => {
- it('should export a \'recommended\' configuration', () => {
- expect(plugin.configs.recommended).toBeDefined();
- });
+test('configurations', (t) => {
+ t.notEqual(plugin.configs.recommended, undefined, 'exports a \'recommended\' configuration');
+
+ t.end();
});
-describe('schemas', () => {
+test('schemas', (t) => {
rules.forEach((ruleName) => {
- it(`${ruleName} should export a schema with type object`, () => {
- const rule = require(path.join('../src/rules', ruleName)); // eslint-disable-line
- const schema = rule.meta && rule.meta.schema && rule.meta.schema[0];
- const { type } = schema;
+ const rule = require(path.join('../src/rules', ruleName)); // eslint-disable-line import/no-dynamic-require
+ const schema = rule.meta && rule.meta.schema && rule.meta.schema[0];
+ const { type } = schema;
- expect(type).toEqual('object');
- });
+ t.equal(type, 'object', `${ruleName} exports a schema with type object`);
});
+
+ t.end();
});
diff --git a/__tests__/src/util/implicitRoles/input-test.js b/__tests__/src/util/implicitRoles/input-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/implicitRoles/input-test.js
+++ b/__tests__/src/util/implicitRoles/input-test.js
@@ -1,34 +1,87 @@
-import expect from 'expect';
+import test from 'tape';
+
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForInput from '../../../../src/util/implicitRoles/input';
-describe('isAbstractRole', () => {
- it('works for buttons', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'button')])).toBe('button');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'image')])).toBe('button');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'reset')])).toBe('button');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'submit')])).toBe('button');
+test('isAbstractRole', (t) => {
+ t.test('works for buttons', (st) => {
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'button')]),
+ 'button',
+ );
+
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'image')]),
+ 'button',
+ );
+
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'reset')]),
+ 'button',
+ );
+
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'submit')]),
+ 'button',
+ );
+
+ st.end();
});
- it('works for checkboxes', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'checkbox')])).toBe('checkbox');
+
+ t.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'checkbox')]),
+ 'checkbox',
+ 'works for checkboxes',
+ );
+
+ t.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'radio')]),
+ 'radio',
+ 'works for radios',
+ );
+
+ t.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'range')]),
+ 'slider',
+ 'works for ranges',
+ );
+
+ t.test('works for textboxes', (st) => {
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'email')]),
+ 'textbox',
+ );
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'password')]),
+ 'textbox',
+ );
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'search')]),
+ 'textbox',
+ );
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'tel')]),
+ 'textbox',
+ );
+ st.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', 'url')]),
+ 'textbox',
+ );
+
+ st.end();
});
- it('works for radios', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'radio')])).toBe('radio');
- });
- it('works for ranges', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'range')])).toBe('slider');
- });
- it('works for textboxes', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'email')])).toBe('textbox');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'password')])).toBe('textbox');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'search')])).toBe('textbox');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'tel')])).toBe('textbox');
- expect(getImplicitRoleForInput([JSXAttributeMock('type', 'url')])).toBe('textbox');
- });
- it('works for the default case', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', '')])).toBe('textbox');
- });
- it('works for the true case', () => {
- expect(getImplicitRoleForInput([JSXAttributeMock('type', true)])).toBe('textbox');
- });
+
+ t.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', '')]),
+ 'textbox',
+ 'works for the default case',
+ );
+
+ t.equal(
+ getImplicitRoleForInput([JSXAttributeMock('type', true)]),
+ 'textbox',
+ 'works for the true case',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/isAbstractRole-test.js b/__tests__/src/util/isAbstractRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isAbstractRole-test.js
+++ b/__tests__/src/util/isAbstractRole-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isAbstractRole from '../../../src/util/isAbstractRole';
import {
@@ -8,32 +9,43 @@
} from '../../../__mocks__/genInteractives';
-describe('isAbstractRole', () => {
- describe('JSX Components (no tagName)', () => {
- it('should NOT identify them as abstract role elements', () => {
- expect(isAbstractRole(undefined, []))
- .toBe(false);
- });
- });
- describe('elements with an abstract role', () => {
+test('isAbstractRole', (t) => {
+ t.equal(
+ isAbstractRole(undefined, []),
+ false,
+ 'does NOT identify JSX Components (no tagName) as abstract role elements',
+ );
+
+ t.test('elements with an abstract role', (st) => {
genAbstractRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should identify \`${genElementSymbol(openingElement)}\` as an abstract role element`, () => {
- expect(isAbstractRole(
+ st.equal(
+ isAbstractRole(
elementType(openingElement),
attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as an abstract role element`,
+ );
});
+
+ st.end();
});
- describe('elements with a non-abstract role', () => {
+
+ t.test('elements with a non-abstract role', (st) => {
genNonAbstractRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an abstract role element`, () => {
- expect(isAbstractRole(
+ st.equal(
+ isAbstractRole(
elementType(openingElement),
attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` as an abstract role element`,
+ );
});
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isContentEditable-test.js b/__tests__/src/util/isContentEditable-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isContentEditable-test.js
+++ b/__tests__/src/util/isContentEditable-test.js
@@ -1,51 +1,52 @@
-import expect from 'expect';
+import test from 'tape';
+
import isContentEditable from '../../../src/util/isContentEditable';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('isContentEditable', () => {
- describe('HTML5', () => {
- describe('content editable', () => {
- it('should identify HTML5 contentEditable elements', () => {
- const attributes = [
- JSXAttributeMock('contentEditable', 'true'),
- ];
- expect(isContentEditable('some tag', attributes))
- .toBe(true);
- });
- });
+test('isContentEditable - HTML5', (t) => {
+ t.equal(
+ isContentEditable('some tag', [
+ JSXAttributeMock('contentEditable', 'true'),
+ ]),
+ true,
+ 'identifies HTML5 contentEditable elements',
+ );
- describe('not content editable', () => {
- it('should not identify HTML5 content editable elements with null as the value', () => {
- const attributes = [
- JSXAttributeMock('contentEditable', null),
- ];
- expect(isContentEditable('some tag', attributes))
- .toBe(false);
- });
+ t.test('not content editable', (st) => {
+ st.equal(
+ isContentEditable('some tag', [
+ JSXAttributeMock('contentEditable', null),
+ ]),
+ false,
+ 'does not identify HTML5 content editable elements with null as the value',
+ );
- it('should not identify HTML5 content editable elements with undefined as the value', () => {
- const attributes = [
- JSXAttributeMock('contentEditable', undefined),
- ];
- expect(isContentEditable('some tag', attributes))
- .toBe(false);
- });
+ st.equal(
+ isContentEditable('some tag', [
+ JSXAttributeMock('contentEditable', undefined),
+ ]),
+ false,
+ 'does not identify HTML5 content editable elements with undefined as the value',
+ );
- it('should not identify HTML5 content editable elements with true as the value', () => {
- const attributes = [
- JSXAttributeMock('contentEditable', true),
- ];
- expect(isContentEditable('some tag', attributes))
- .toBe(false);
- });
+ st.equal(
+ isContentEditable('some tag', [
+ JSXAttributeMock('contentEditable', true),
+ ]),
+ false,
+ 'does not identify HTML5 content editable elements with true as the value',
+ );
- it('should not identify HTML5 content editable elements with "false" as the value', () => {
- const attributes = [
- JSXAttributeMock('contentEditable', 'false'),
- ];
- expect(isContentEditable('some tag', attributes))
- .toBe(false);
- });
- });
+ st.equal(
+ isContentEditable('some tag', [
+ JSXAttributeMock('contentEditable', 'false'),
+ ]),
+ false,
+ 'does not identify HTML5 content editable elements with "false" as the value',
+ );
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isDisabledElement-test.js b/__tests__/src/util/isDisabledElement-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isDisabledElement-test.js
+++ b/__tests__/src/util/isDisabledElement-test.js
@@ -1,81 +1,88 @@
-import expect from 'expect';
+import test from 'tape';
+
import isDisabledElement from '../../../src/util/isDisabledElement';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('isDisabledElement', () => {
- describe('HTML5', () => {
- describe('disabled', () => {
- it('should identify HTML5 disabled elements', () => {
- const attributes = [
- JSXAttributeMock('disabled', 'disabled'),
- ];
- expect(isDisabledElement(attributes))
- .toBe(true);
- });
- });
- describe('not disabled', () => {
- it('should identify HTML5 disabled elements with null as the value', () => {
- const attributes = [
- JSXAttributeMock('disabled', null),
- ];
- expect(isDisabledElement(attributes))
- .toBe(true);
- });
- it('should not identify HTML5 disabled elements with undefined as the value', () => {
- const attributes = [
- JSXAttributeMock('disabled', undefined),
- ];
- expect(isDisabledElement(attributes))
- .toBe(false);
- });
- });
+test('isDisabledElement', (t) => {
+ t.test('HTML5', (st) => {
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('disabled', 'disabled'),
+ ]),
+ true,
+ 'identifies HTML5 disabled elements',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('disabled', null),
+ ]),
+ true,
+ 'identifies HTML5 disabled elements with null as the value',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('disabled', undefined),
+ ]),
+ false,
+ 'does not identify HTML5 disabled elements with undefined as the value',
+ );
+
+ st.end();
});
- describe('ARIA', () => {
- describe('disabled', () => {
- it('should not identify ARIA disabled elements', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', 'true'),
- ];
- expect(isDisabledElement(attributes))
- .toBe(true);
- });
- it('should not identify ARIA disabled elements', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', true),
- ];
- expect(isDisabledElement(attributes))
- .toBe(true);
- });
- });
- describe('not disabled', () => {
- it('should not identify ARIA disabled elements', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', 'false'),
- ];
- expect(isDisabledElement(attributes))
- .toBe(false);
- });
- it('should not identify ARIA disabled elements', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', false),
- ];
- expect(isDisabledElement(attributes))
- .toBe(false);
- });
- it('should not identify ARIA disabled elements with null as the value', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', null),
- ];
- expect(isDisabledElement(attributes))
- .toBe(false);
- });
- it('should not identify ARIA disabled elements with undefined as the value', () => {
- const attributes = [
- JSXAttributeMock('aria-disabled', undefined),
- ];
- expect(isDisabledElement(attributes))
- .toBe(false);
- });
- });
+
+ t.test('ARIA', (st) => {
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', 'true'),
+ ]),
+ true,
+ 'does not identify ARIA disabled elements',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', true),
+ ]),
+ true,
+ 'does not identify ARIA disabled elements',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', 'false'),
+ ]),
+ false,
+ 'does not identify ARIA disabled elements',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', false),
+ ]),
+ false,
+ 'does not identify ARIA disabled elements',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', null),
+ ]),
+ false,
+ 'does not identify ARIA disabled elements with null as the value',
+ );
+
+ st.equal(
+ isDisabledElement([
+ JSXAttributeMock('aria-disabled', undefined),
+ ]),
+ false,
+ 'does not identify ARIA disabled elements with undefined as the value',
+ );
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isDOMElement-test.js b/__tests__/src/util/isDOMElement-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isDOMElement-test.js
+++ b/__tests__/src/util/isDOMElement-test.js
@@ -1,24 +1,30 @@
-import expect from 'expect';
+import test from 'tape';
import { dom } from 'aria-query';
import { elementType } from 'jsx-ast-utils';
+
import isDOMElement from '../../../src/util/isDOMElement';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
-describe('isDOMElement', () => {
- describe('DOM elements', () => {
+test('isDOMElement', (t) => {
+ t.test('DOM elements', (st) => {
dom.forEach((_, el) => {
- it(`should identify ${el} as a DOM element`, () => {
- const element = JSXElementMock(el);
- expect(isDOMElement(elementType(element.openingElement)))
- .toBe(true);
- });
+ const element = JSXElementMock(el);
+
+ st.equal(
+ isDOMElement(elementType(element.openingElement)),
+ true,
+ `identifies ${el} as a DOM element`,
+ );
});
+
+ st.end();
});
- describe('Custom Element', () => {
- it('should not identify a custom element', () => {
- const element = JSXElementMock('CustomElement');
- expect(isDOMElement(element))
- .toBe(false);
- });
- });
+
+ t.equal(
+ isDOMElement(JSXElementMock('CustomElement')),
+ false,
+ 'does not identify a custom element',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/isFocusable-test.js b/__tests__/src/util/isFocusable-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isFocusable-test.js
+++ b/__tests__/src/util/isFocusable-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isFocusable from '../../../src/util/isFocusable';
import {
@@ -13,74 +14,98 @@
}
-describe('isFocusable', () => {
- describe('interactive elements', () => {
+test('isFocusable', (t) => {
+ t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
- it(`should identify \`${genElementSymbol(openingElement)}\` as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
openingElement.attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as a focusable element`,
+ );
- it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`,
+ );
- it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`,
+ );
- it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`,
+ );
});
+
+ st.end();
});
- describe('non-interactive elements', () => {
+ t.test('non-interactive elements', (st) => {
genNonInteractiveElements().forEach(({ openingElement }) => {
- it(`should not identify \`${genElementSymbol(openingElement)}\` as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` as a focusable element`,
+ );
- it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`,
+ );
- it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`,
+ );
- it(`should identify \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`,
+ );
- it(`should not identify \`${genElementSymbol(openingElement)}\` with tabIndex of 'bogus' as a focusable element`, () => {
- expect(isFocusable(
+ st.equal(
+ isFocusable(
elementType(openingElement),
mergeTabIndex('bogus', openingElement.attributes),
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of 'bogus' as a focusable element`,
+ );
});
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isInteractiveElement-test.js b/__tests__/src/util/isInteractiveElement-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isInteractiveElement-test.js
+++ b/__tests__/src/util/isInteractiveElement-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isInteractiveElement from '../../../src/util/isInteractiveElement';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
@@ -12,65 +13,92 @@
} from '../../../__mocks__/genInteractives';
-describe('isInteractiveElement', () => {
- describe('JSX Components (no tagName)', () => {
- it('should identify them as interactive elements', () => {
- expect(isInteractiveElement(undefined, []))
- .toBe(false);
- });
- });
- describe('interactive elements', () => {
+test('isInteractiveElement', (t) => {
+ t.equal(
+ isInteractiveElement(undefined, []),
+ false,
+ 'identifies them as interactive elements',
+ );
+
+ t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
- it(`should identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
- expect(isInteractiveElement(
+ st.equal(
+ isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
+ );
});
+
+ st.end();
});
- describe('interactive role elements', () => {
+
+ t.test('interactive role elements', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
- expect(isInteractiveElement(
+ st.equal(
+ isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
+ );
});
+
+ st.end();
});
- describe('non-interactive elements', () => {
+
+ t.test('non-interactive elements', (st) => {
genNonInteractiveElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
- expect(isInteractiveElement(
+ st.equal(
+ isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
+ );
});
+
+ st.end();
});
- describe('non-interactive role elements', () => {
+
+ t.test('non-interactive role elements', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as an interactive element`, () => {
- expect(isInteractiveElement(
+ st.equal(
+ isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
+ );
});
+
+ st.end();
});
- describe('indeterminate elements', () => {
+
+ t.test('indeterminate elements', (st) => {
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${openingElement.name.name}\` as an interactive element`, () => {
- expect(isInteractiveElement(
+ st.equal(
+ isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
+ );
});
+
+ st.end();
});
- describe('JSX elements', () => {
- it('is not interactive', () => {
- expect(isInteractiveElement('CustomComponent', JSXElementMock())).toBe(false);
- });
- });
+
+ t.equal(
+ isInteractiveElement('CustomComponent', JSXElementMock()),
+ false,
+ 'JSX elements are not interactive',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/isInteractiveRole-test.js b/__tests__/src/util/isInteractiveRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isInteractiveRole-test.js
+++ b/__tests__/src/util/isInteractiveRole-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isInteractiveRole from '../../../src/util/isInteractiveRole';
import {
@@ -8,37 +9,51 @@
} from '../../../__mocks__/genInteractives';
-describe('isInteractiveRole', () => {
- describe('JSX Components (no tagName)', () => {
- it('should identify them as interactive role elements', () => {
- expect(isInteractiveRole(undefined, []))
- .toBe(false);
- });
- });
- describe('elements with a non-interactive role', () => {
+test('isInteractiveRole', (t) => {
+ t.equal(
+ isInteractiveRole(undefined, []),
+ false,
+ 'identifies JSX Components (no tagName) as interactive role elements',
+ );
+
+ t.test('elements with a non-interactive role', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should not identify \`${genElementSymbol(openingElement)}\` as an interactive role element`, () => {
- expect(isInteractiveRole(
+
+ st.equal(
+ isInteractiveRole(
elementType(openingElement),
attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` as an interactive role element`,
+ );
});
+
+ st.end();
});
- describe('elements without a role', () => {
- it('should not identify them as interactive role elements', () => {
- expect(isInteractiveRole('div', [])).toBe(false);
- });
- });
- describe('elements with an interactive role', () => {
+
+ t.equal(
+ isInteractiveRole('div', []),
+ false,
+ 'does NOT identify elements without a role as interactive role elements',
+ );
+
+ t.test('elements with an interactive role', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should identify \`${genElementSymbol(openingElement)}\` as an interactive role element`, () => {
- expect(isInteractiveRole(
+
+ st.equal(
+ isInteractiveRole(
elementType(openingElement),
attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as an interactive role element`,
+ );
});
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isNonInteractiveElement-test.js b/__tests__/src/util/isNonInteractiveElement-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isNonInteractiveElement-test.js
+++ b/__tests__/src/util/isNonInteractiveElement-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isNonInteractiveElement from '../../../src/util/isNonInteractiveElement';
import {
@@ -11,60 +12,86 @@
} from '../../../__mocks__/genInteractives';
-describe('isNonInteractiveElement', () => {
- describe('JSX Components (no tagName)', () => {
- it('should identify them as interactive elements', () => {
- expect(isNonInteractiveElement(undefined, []))
- .toBe(false);
- });
- });
- describe('non-interactive elements', () => {
+test('isNonInteractiveElement', (t) => {
+ t.equal(
+ isNonInteractiveElement(undefined, []),
+ false,
+ 'identifies JSX Components (no tagName) as non-interactive elements',
+ );
+
+ t.test('non-interactive elements', (st) => {
genNonInteractiveElements().forEach(({ openingElement }) => {
- it(`should identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
- expect(isNonInteractiveElement(
+ st.equal(
+ isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
+ );
});
+
+ st.end();
});
- describe('non-interactive role elements', () => {
+
+ t.test('non-interactive role elements', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
- expect(isNonInteractiveElement(
+ st.equal(
+ isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
+ );
});
+
+ st.end();
});
- describe('interactive elements', () => {
+
+ t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
- expect(isNonInteractiveElement(
+ st.equal(
+ isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
+ );
});
+
+ st.end();
});
- describe('interactive role elements', () => {
+
+ t.test('interactive role elements', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive element`, () => {
- expect(isNonInteractiveElement(
+ st.equal(
+ isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
+ );
});
+
+ st.end();
});
- describe('indeterminate elements', () => {
+
+ t.test('indeterminate elements', (st) => {
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
- it(`should NOT identify \`${openingElement.name.name}\` as a non-interactive element`, () => {
- expect(isNonInteractiveElement(
+ st.equal(
+ isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
+ );
});
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isNonInteractiveRole-test.js b/__tests__/src/util/isNonInteractiveRole-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isNonInteractiveRole-test.js
+++ b/__tests__/src/util/isNonInteractiveRole-test.js
@@ -1,4 +1,5 @@
-import expect from 'expect';
+import test from 'tape';
import { elementType } from 'jsx-ast-utils';
+
import isNonInteractiveRole from '../../../src/util/isNonInteractiveRole';
import {
@@ -8,37 +9,51 @@
} from '../../../__mocks__/genInteractives';
-describe('isNonInteractiveRole', () => {
- describe('JSX Components (no tagName)', () => {
- it('should identify them as interactive role elements', () => {
- expect(isNonInteractiveRole(undefined, []))
- .toBe(false);
- });
- });
- describe('elements with a non-interactive role', () => {
+test('isNonInteractiveRole', (t) => {
+ t.equal(
+ isNonInteractiveRole(undefined, []),
+ false,
+ 'identifies JSX Components (no tagName) as non-interactive elements',
+ );
+
+ t.test('elements with a non-interactive role', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should identify \`${genElementSymbol(openingElement)}\` as non-interactive role element`, () => {
- expect(isNonInteractiveRole(
+
+ st.equal(
+ isNonInteractiveRole(
elementType(openingElement),
attributes,
- )).toBe(true);
- });
+ ),
+ true,
+ `identifies \`${genElementSymbol(openingElement)}\` as a non-interactive role element`,
+ );
});
+
+ st.end();
});
- describe('elements without a role', () => {
- it('should not identify them as non-interactive role elements', () => {
- expect(isNonInteractiveRole('div', [])).toBe(false);
- });
- });
- describe('elements with an interactive role', () => {
+
+ t.equal(
+ isNonInteractiveRole('div', []),
+ false,
+ 'does NOT identify elements without a role as non-interactive role elements',
+ );
+
+ t.test('elements with an interactive role', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
- it(`should NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive role element`, () => {
- expect(isNonInteractiveRole(
+
+ st.equal(
+ isNonInteractiveRole(
elementType(openingElement),
attributes,
- )).toBe(false);
- });
+ ),
+ false,
+ `does NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive role element`,
+ );
});
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/isNonLiteralProperty-test.js b/__tests__/src/util/isNonLiteralProperty-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isNonLiteralProperty-test.js
+++ b/__tests__/src/util/isNonLiteralProperty-test.js
@@ -1,3 +1,4 @@
-import expect from 'expect';
+import test from 'tape';
+
import isNonLiteralProperty from '../../../src/util/isNonLiteralProperty';
import IdentifierMock from '../../../__mocks__/IdentifierMock';
@@ -11,34 +12,41 @@
const spread = JSXSpreadAttributeMock('theSpread');
-describe('isNonLiteralProperty', () => {
- describe('elements without the property', () => {
- it('should not identify them as non-literal role elements', () => {
- expect(isNonLiteralProperty([], theProp)).toBe(false);
- });
- });
- describe('elements with a literal property', () => {
- it('should not identify them as non-literal role elements without spread operator', () => {
- expect(isNonLiteralProperty([JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp)).toBe(false);
- });
- it('should not identify them as non-literal role elements with spread operator', () => {
- expect(isNonLiteralProperty([spread, JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp)).toBe(false);
- });
- });
- describe('elements with a JSXText property', () => {
- it('should not identify them as non-literal role elements', () => {
- expect(isNonLiteralProperty([JSXAttributeMock(theProp, JSXTextMock('theRole'))], theProp)).toBe(false);
- });
- });
- describe('elements with a property of undefined', () => {
- it('should not identify them as non-literal role elements', () => {
- const undefinedExpression = IdentifierMock('undefined');
- expect(isNonLiteralProperty([JSXAttributeMock(theProp, undefinedExpression)], theProp)).toBe(false);
- });
- });
- describe('elements with a expression property', () => {
- it('should identify them as non-literal role elements', () => {
- const identifierExpression = IdentifierMock('theIdentifier');
- expect(isNonLiteralProperty([JSXAttributeMock(theProp, identifierExpression)], theProp)).toBe(true);
- });
- });
+test('isNonLiteralProperty', (t) => {
+ t.equal(
+ isNonLiteralProperty([], theProp),
+ false,
+ 'does not identify them as non-literal role elements',
+ );
+
+ t.equal(
+ isNonLiteralProperty([JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp),
+ false,
+ 'does not identify elements with a literal property as non-literal role elements without spread operator',
+ );
+
+ t.equal(
+ isNonLiteralProperty([spread, JSXAttributeMock(theProp, LiteralMock('theRole'))], theProp),
+ false,
+ 'does not identify elements with a literal property as non-literal role elements with spread operator',
+ );
+
+ t.equal(
+ isNonLiteralProperty([JSXAttributeMock(theProp, JSXTextMock('theRole'))], theProp),
+ false,
+ 'identifies elements with a JSXText property as non-literal role elements',
+ );
+
+ t.equal(
+ isNonLiteralProperty([JSXAttributeMock(theProp, IdentifierMock('undefined'))], theProp),
+ false,
+ 'does not identify elements with a property of undefined as non-literal role elements',
+ );
+
+ t.equal(
+ isNonLiteralProperty([JSXAttributeMock(theProp, IdentifierMock('theIdentifier'))], theProp),
+ true,
+ 'identifies elements with an expression property as non-literal role elements',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/isSemanticRoleElement-test.js b/__tests__/src/util/isSemanticRoleElement-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/isSemanticRoleElement-test.js
+++ b/__tests__/src/util/isSemanticRoleElement-test.js
@@ -1,32 +1,54 @@
-import expect from 'expect';
+import test from 'tape';
+
import isSemanticRoleElement from '../../../src/util/isSemanticRoleElement';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
-describe('isSemanticRoleElement', () => {
- it('should identify semantic role elements', () => {
- expect(isSemanticRoleElement('input', [
+test('isSemanticRoleElement', (t) => {
+ t.equal(
+ isSemanticRoleElement('input', [
JSXAttributeMock('type', 'checkbox'),
JSXAttributeMock('role', 'switch'),
- ])).toBe(true);
+ ]),
+ true,
+ 'identifies semantic role elements',
+ );
+
+ t.test('rejects non-semantics role elements', (st) => {
+ st.equal(
+ isSemanticRoleElement('input', [
+ JSXAttributeMock('type', 'radio'),
+ JSXAttributeMock('role', 'switch'),
+ ]),
+ false,
+ );
+
+ st.equal(
+ isSemanticRoleElement('input', [
+ JSXAttributeMock('type', 'text'),
+ JSXAttributeMock('role', 'combobox'),
+ ]),
+ false,
+ );
+
+ st.equal(
+ isSemanticRoleElement('button', [
+ JSXAttributeMock('role', 'switch'),
+ JSXAttributeMock('aria-pressed', 'true'),
+ ]),
+ false,
+ );
+
+ st.equal(
+ isSemanticRoleElement('input', [
+ JSXAttributeMock('role', 'switch'),
+ ]),
+ false,
+ );
+
+ st.end();
});
- it('should reject non-semantic role elements', () => {
- expect(isSemanticRoleElement('input', [
- JSXAttributeMock('type', 'radio'),
- JSXAttributeMock('role', 'switch'),
- ])).toBe(false);
- expect(isSemanticRoleElement('input', [
- JSXAttributeMock('type', 'text'),
- JSXAttributeMock('role', 'combobox'),
- ])).toBe(false);
- expect(isSemanticRoleElement('button', [
- JSXAttributeMock('role', 'switch'),
- JSXAttributeMock('aria-pressed', 'true'),
- ])).toBe(false);
- expect(isSemanticRoleElement('input', [
- JSXAttributeMock('role', 'switch'),
- ])).toBe(false);
- });
- it('should not throw on JSXSpreadAttribute', () => {
- expect(() => {
+
+ t.doesNotThrow(
+ () => {
isSemanticRoleElement('input', [
JSXAttributeMock('type', 'checkbox'),
@@ -43,5 +65,8 @@
},
]);
- }).not.toThrow();
- });
+ },
+ 'does not throw on JSXSpreadAttribute',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/rules/label-has-associated-control-test.js b/__tests__/src/rules/label-has-associated-control-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/label-has-associated-control-test.js
+++ b/__tests__/src/rules/label-has-associated-control-test.js
@@ -27,4 +27,9 @@
};
+const expectedErrorNoLabel = {
+ message: 'A form label must have accessible text.',
+ type: 'JSXOpeningElement',
+};
+
const componentsSettings = {
'jsx-a11y': {
@@ -36,4 +41,12 @@
};
+const attributesSettings = {
+ 'jsx-a11y': {
+ attributes: {
+ for: ['htmlFor', 'for'],
+ },
+ },
+};
+
const htmlForValid = [
{ code: '<label htmlFor="js_id"><span><span><span>A label</span></span></span></label>', options: [{ depth: 4 }] },
@@ -41,4 +54,8 @@
{ code: '<label htmlFor="js_id" aria-labelledby="A label" />' },
{ code: '<div><label htmlFor="js_id">A label</label><input id="js_id" /></div>' },
+ { code: '<label for="js_id"><span><span><span>A label</span></span></span></label>', options: [{ depth: 4 }], settings: attributesSettings },
+ { code: '<label for="js_id" aria-label="A label" />', settings: attributesSettings },
+ { code: '<label for="js_id" aria-labelledby="A label" />', settings: attributesSettings },
+ { code: '<div><label for="js_id">A label</label><input id="js_id" /></div>', settings: attributesSettings },
// Custom label component.
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ labelComponents: ['CustomLabel'] }] },
@@ -50,4 +67,6 @@
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['Custom*'] }] },
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['*Label'] }] },
+ // Rule does not error if presence of accessible label cannot be determined
+ { code: '<div><label htmlFor="js_id"><CustomText /></label><input id="js_id" /></div>' },
];
const nestingValid = [
@@ -75,4 +94,6 @@
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['Custom*'] }] },
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['*Input'] }] },
+ // Rule does not error if presence of accessible label cannot be determined
+ { code: '<label><CustomText /><input /></label>' },
];
@@ -129,10 +150,10 @@
const neverValid = [
- { code: '<label htmlFor="js_id" />', errors: [expectedError] },
- { code: '<label htmlFor="js_id"><input /></label>', errors: [expectedError] },
- { code: '<label htmlFor="js_id"><textarea /></label>', errors: [expectedError] },
- { code: '<label></label>', errors: [expectedError] },
+ { code: '<label htmlFor="js_id" />', errors: [expectedErrorNoLabel] },
+ { code: '<label htmlFor="js_id"><input /></label>', errors: [expectedErrorNoLabel] },
+ { code: '<label htmlFor="js_id"><textarea /></label>', errors: [expectedErrorNoLabel] },
+ { code: '<label></label>', errors: [expectedErrorNoLabel] },
{ code: '<label>A label</label>', errors: [expectedError] },
- { code: '<div><label /><input /></div>', errors: [expectedError] },
+ { code: '<div><label /><input /></div>', errors: [expectedErrorNoLabel] },
{ code: '<div><label>A label</label><input /></div>', errors: [expectedError] },
// Custom label component.
@@ -143,9 +164,9 @@
{ code: '<label label="A label" />', options: [{ labelAttributes: ['label'] }], errors: [expectedError] },
// Custom controlComponents.
- { code: '<label><span><CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedError] },
- { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedError] },
- { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedError] },
- { code: '<label><span><CustomInput /></span></label>', settings: componentsSettings, errors: [expectedError] },
- { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', settings: componentsSettings, errors: [expectedError] },
+ { code: '<label><span><CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }], errors: [expectedErrorNoLabel] },
+ { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }], errors: [expectedErrorNoLabel] },
+ { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }], errors: [expectedErrorNoLabel] },
+ { code: '<label><span><CustomInput /></span></label>', settings: componentsSettings, errors: [expectedErrorNoLabel] },
+ { code: '<CustomLabel><span><CustomInput /></span></CustomLabel>', settings: componentsSettings, errors: [expectedErrorNoLabel] },
];
// htmlFor valid
diff --git a/lib/rules/label-has-associated-control.js b/lib/rules/label-has-associated-control.js
index v6.9.0..v6.10.0 100644
--- a/lib/rules/label-has-associated-control.js
+++ b/lib/rules/label-has-associated-control.js
@@ -23,4 +23,5 @@
var errorMessage = 'A form label must be associated with a control.';
+var errorMessageNoLabel = 'A form label must have accessible text.';
var schema = (0, _schemas.generateObjSchema)({
labelComponents: _schemas.arraySchema,
@@ -38,9 +39,18 @@
}
});
-var validateId = function validateId(node) {
- var htmlForAttr = (0, _jsxAstUtils.getProp)(node.attributes, 'htmlFor');
- var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
- return htmlForAttr !== false && !!htmlForValue;
-};
+function validateID(node, context) {
+ var _settings$jsxA11y$at, _settings$jsxA11y, _settings$jsxA11y$att;
+ var settings = context.settings;
+ var htmlForAttributes = (_settings$jsxA11y$at = (_settings$jsxA11y = settings['jsx-a11y']) === null || _settings$jsxA11y === void 0 ? void 0 : (_settings$jsxA11y$att = _settings$jsxA11y.attributes) === null || _settings$jsxA11y$att === void 0 ? void 0 : _settings$jsxA11y$att["for"]) !== null && _settings$jsxA11y$at !== void 0 ? _settings$jsxA11y$at : ['htmlFor'];
+ for (var i = 0; i < htmlForAttributes.length; i += 1) {
+ var attribute = htmlForAttributes[i];
+ if ((0, _jsxAstUtils.hasProp)(node.attributes, attribute)) {
+ var htmlForAttr = (0, _jsxAstUtils.getProp)(node.attributes, attribute);
+ var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
+ return htmlForAttr !== false && !!htmlForValue;
+ }
+ }
+ return false;
+}
var _default = exports["default"] = {
meta: {
@@ -64,36 +74,41 @@
// Prevent crazy recursion.
var recursionDepth = Math.min(options.depth === undefined ? 2 : options.depth, 25);
- var hasLabelId = validateId(node.openingElement);
+ var hasLabelId = validateID(node.openingElement, context);
// Check for multiple control components.
var hasNestedControl = controlComponents.some(function (name) {
return (0, _mayContainChildComponent["default"])(node, name, recursionDepth, elementType);
});
- var hasAccessibleLabel = (0, _mayHaveAccessibleLabel["default"])(node, recursionDepth, options.labelAttributes);
- if (hasAccessibleLabel) {
- switch (assertType) {
- case 'htmlFor':
- if (hasLabelId) {
- return;
- }
- break;
- case 'nesting':
- if (hasNestedControl) {
- return;
- }
- break;
- case 'both':
- if (hasLabelId && hasNestedControl) {
- return;
- }
- break;
- case 'either':
- if (hasLabelId || hasNestedControl) {
- return;
- }
- break;
- default:
- break;
- }
+ var hasAccessibleLabel = (0, _mayHaveAccessibleLabel["default"])(node, recursionDepth, options.labelAttributes, elementType, controlComponents);
+ if (!hasAccessibleLabel) {
+ context.report({
+ node: node.openingElement,
+ message: errorMessageNoLabel
+ });
+ return;
}
+ switch (assertType) {
+ case 'htmlFor':
+ if (hasLabelId) {
+ return;
+ }
+ break;
+ case 'nesting':
+ if (hasNestedControl) {
+ return;
+ }
+ break;
+ case 'both':
+ if (hasLabelId && hasNestedControl) {
+ return;
+ }
+ break;
+ case 'either':
+ if (hasLabelId || hasNestedControl) {
+ return;
+ }
+ break;
+ default:
+ break;
+ }
// htmlFor case
diff --git a/__tests__/src/rules/label-has-for-test.js b/__tests__/src/rules/label-has-for-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/label-has-for-test.js
+++ b/__tests__/src/rules/label-has-for-test.js
@@ -51,4 +51,12 @@
}];
+const attributesSettings = {
+ 'jsx-a11y': {
+ attributes: {
+ for: ['htmlFor', 'for'],
+ },
+ },
+};
+
ruleTester.run('label-has-for', rule, {
valid: parsers.all([].concat(
@@ -57,8 +65,12 @@
{ code: '<label htmlFor="foo"><input /></label>' },
{ code: '<label htmlFor="foo"><textarea /></label>' },
+ { code: '<label for="foo"><input /></label>', settings: attributesSettings },
+ { code: '<label for="foo"><textarea /></label>', settings: attributesSettings },
{ code: '<Label />' }, // lower-case convention refers to real HTML elements.
{ code: '<Label htmlFor="foo" />' },
+ { code: '<Label for="foo" />', settings: attributesSettings },
{ code: '<Descriptor />' },
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>' },
+ { code: '<Descriptor for="foo">Test!</Descriptor>', settings: attributesSettings },
{ code: '<UX.Layout>test</UX.Layout>' },
diff --git a/lib/rules/label-has-for.js b/lib/rules/label-has-for.js
index v6.9.0..v6.10.0 100644
--- a/lib/rules/label-has-for.js
+++ b/lib/rules/label-has-for.js
@@ -56,10 +56,20 @@
return false;
}
-var validateId = function validateId(node) {
- var htmlForAttr = (0, _jsxAstUtils.getProp)(node.attributes, 'htmlFor');
- var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
- return htmlForAttr !== false && !!htmlForValue;
-};
-var validate = function validate(node, required, allowChildren, elementType) {
+function validateID(_ref, context) {
+ var _settings$jsxA11y$at, _settings$jsxA11y, _settings$jsxA11y$att;
+ var attributes = _ref.attributes;
+ var settings = context.settings;
+ var htmlForAttributes = (_settings$jsxA11y$at = (_settings$jsxA11y = settings['jsx-a11y']) === null || _settings$jsxA11y === void 0 ? void 0 : (_settings$jsxA11y$att = _settings$jsxA11y.attributes) === null || _settings$jsxA11y$att === void 0 ? void 0 : _settings$jsxA11y$att["for"]) !== null && _settings$jsxA11y$at !== void 0 ? _settings$jsxA11y$at : ['htmlFor'];
+ for (var i = 0; i < htmlForAttributes.length; i += 1) {
+ var attribute = htmlForAttributes[i];
+ if ((0, _jsxAstUtils.hasProp)(attributes, attribute)) {
+ var htmlForAttr = (0, _jsxAstUtils.getProp)(attributes, attribute);
+ var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
+ return htmlForAttr !== false && !!htmlForValue;
+ }
+ }
+ return false;
+}
+function validate(node, required, allowChildren, elementType, context) {
if (allowChildren === true) {
return (0, _hasAccessibleChild["default"])(node.parent, elementType);
@@ -68,10 +78,10 @@
return validateNesting(node);
}
- return validateId(node);
-};
-var getValidityStatus = function getValidityStatus(node, required, allowChildren, elementType) {
+ return validateID(node, context);
+}
+function getValidityStatus(node, required, allowChildren, elementType, context) {
if (Array.isArray(required.some)) {
var _isValid = required.some.some(function (rule) {
- return validate(node, rule, allowChildren, elementType);
+ return validate(node, rule, allowChildren, elementType, context);
});
var _message = !_isValid ? "Form label must have ANY of the following types of associated control: ".concat(required.some.join(', ')) : null;
@@ -83,5 +93,5 @@
if (Array.isArray(required.every)) {
var _isValid2 = required.every.every(function (rule) {
- return validate(node, rule, allowChildren, elementType);
+ return validate(node, rule, allowChildren, elementType, context);
});
var _message2 = !_isValid2 ? "Form label must have ALL of the following types of associated control: ".concat(required.every.join(', ')) : null;
@@ -91,5 +101,5 @@
};
}
- var isValid = validate(node, required, allowChildren, elementType);
+ var isValid = validate(node, required, allowChildren, elementType, context);
var message = !isValid ? "Form label must have the following type of associated control: ".concat(required) : null;
return {
@@ -97,5 +107,5 @@
message
};
-};
+}
var _default = exports["default"] = {
meta: {
@@ -111,5 +121,5 @@
var elementType = (0, _getElementType["default"])(context);
return {
- JSXOpeningElement: function JSXOpeningElement(node) {
+ JSXOpeningElement(node) {
var options = context.options[0] || {};
var componentOptions = options.components || [];
@@ -125,5 +135,5 @@
};
var allowChildren = options.allowChildren || false;
- var _getValidityStatus = getValidityStatus(node, required, allowChildren, elementType),
+ var _getValidityStatus = getValidityStatus(node, required, allowChildren, elementType, context),
isValid = _getValidityStatus.isValid,
message = _getValidityStatus.message;
diff --git a/__tests__/src/util/mayContainChildComponent-test.js b/__tests__/src/util/mayContainChildComponent-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/mayContainChildComponent-test.js
+++ b/__tests__/src/util/mayContainChildComponent-test.js
@@ -1,3 +1,4 @@
-import expect from 'expect';
+import test from 'tape';
+
import mayContainChildComponent from '../../../src/util/mayContainChildComponent';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
@@ -5,47 +6,54 @@
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
-describe('mayContainChildComponent', () => {
- describe('no FancyComponent', () => {
- it('should return false', () => {
- expect(mayContainChildComponent(
+test('mayContainChildComponent', (t) => {
+ t.equal(
+ mayContainChildComponent(
+ JSXElementMock('div', [], [
JSXElementMock('div', [], [
- JSXElementMock('div', [], [
+ JSXElementMock('span', [], []),
+ JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
- JSXElementMock('span', [], [
- JSXElementMock('span', [], []),
- ]),
]),
]),
- JSXElementMock('span', [], []),
- JSXElementMock('img', [
- JSXAttributeMock('src', 'some/path'),
- ]),
]),
- 'FancyComponent',
- 5,
- )).toBe(false);
- });
- });
- describe('contains an indicated component', () => {
- it('should return true', () => {
- expect(mayContainChildComponent(
+ JSXElementMock('span', [], []),
+ JSXElementMock('img', [
+ JSXAttributeMock('src', 'some/path'),
+ ]),
+ ]),
+ 'FancyComponent',
+ 5,
+ ),
+ false,
+ 'no FancyComponent returns false',
+ );
+
+ t.test('contains an indicated component', (st) => {
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('input'),
]),
'input',
- )).toBe(true);
- });
- it('should return true', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'returns true',
+ );
+
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'FancyComponent',
- )).toBe(true);
- });
- it('FancyComponent is outside of default depth, should return false', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'returns true',
+ );
+
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
@@ -54,8 +62,11 @@
]),
'FancyComponent',
- )).toBe(false);
- });
- it('FancyComponent is inside of custom depth, should return true', () => {
- expect(mayContainChildComponent(
+ ),
+ false,
+ 'FancyComponent is outside of default depth, should return false',
+ );
+
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
@@ -65,8 +76,11 @@
'FancyComponent',
2,
- )).toBe(true);
- });
- it('deep nesting, should return true', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'FancyComponent is inside of custom depth, should return true',
+ );
+
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
@@ -90,71 +104,89 @@
'FancyComponent',
6,
- )).toBe(true);
- });
+ ),
+ true,
+ 'deep nesting, returns true',
+ );
+
+ st.end();
});
- describe('Intederminate situations', () => {
- describe('expression container children', () => {
- it('should return true', () => {
- expect(mayContainChildComponent(
- JSXElementMock('div', [], [
- JSXExpressionContainerMock('mysteryBox'),
- ]),
- 'FancyComponent',
- )).toBe(true);
- });
- });
- });
- describe('Glob name matching', () => {
- describe('component name contains question mark ? - match any single character', () => {
- it('should return true', () => {
- expect(mayContainChildComponent(
- JSXElementMock('div', [], [
- JSXElementMock('FancyComponent'),
- ]),
- 'Fanc?Co??onent',
- )).toBe(true);
- });
- it('should return false', () => {
- expect(mayContainChildComponent(
- JSXElementMock('div', [], [
- JSXElementMock('FancyComponent'),
- ]),
- 'FancyComponent?',
- )).toBe(false);
- });
- });
+ t.equal(
+ mayContainChildComponent(
+ JSXElementMock('div', [], [
+ JSXExpressionContainerMock('mysteryBox'),
+ ]),
+ 'FancyComponent',
+ ),
+ true,
+ 'Intederminate situations + expression container children - returns true',
+ );
- describe('component name contains asterisk * - match zero or more characters', () => {
- it('should return true', () => {
- expect(mayContainChildComponent(
+ t.test('Glob name matching - component name contains question mark ? - match any single character', (st) => {
+ st.equal(
+ mayContainChildComponent(
+ JSXElementMock('div', [], [
+ JSXElementMock('FancyComponent'),
+ ]),
+ 'Fanc?Co??onent',
+ ),
+ true,
+ 'returns true',
+ );
+
+ st.equal(
+ mayContainChildComponent(
+ JSXElementMock('div', [], [
+ JSXElementMock('FancyComponent'),
+ ]),
+ 'FancyComponent?',
+ ),
+ false,
+ 'returns false',
+ );
+
+ st.test('component name contains asterisk * - match zero or more characters', (s2t) => {
+ s2t.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'Fancy*',
- )).toBe(true);
- });
- it('should return true', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'returns true',
+ );
+
+ s2t.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'*Component',
- )).toBe(true);
- });
- it('should return true', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'returns true',
+ );
+
+ s2t.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'Fancy*C*t',
- )).toBe(true);
- });
+ ),
+ true,
+ 'returns true',
+ );
+
+ s2t.end();
});
+
+ st.end();
});
- describe('using a custom elementType function', () => {
- it('should return true when the custom elementType returns the proper name', () => {
- expect(mayContainChildComponent(
+ t.test('using a custom elementType function', (st) => {
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('CustomInput'),
@@ -163,8 +195,11 @@
2,
() => 'input',
- )).toBe(true);
- });
- it('should return false when the custom elementType returns a wrong name', () => {
- expect(mayContainChildComponent(
+ ),
+ true,
+ 'returns true when the custom elementType returns the proper name',
+ );
+
+ st.equal(
+ mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('CustomInput'),
@@ -173,6 +208,12 @@
2,
() => 'button',
- )).toBe(false);
- });
+ ),
+ false,
+ 'returns false when the custom elementType returns a wrong name',
+ );
+
+ st.end();
});
+
+ t.end();
});
diff --git a/__tests__/src/util/mayHaveAccessibleLabel-test.js b/__tests__/src/util/mayHaveAccessibleLabel-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/mayHaveAccessibleLabel-test.js
+++ b/__tests__/src/util/mayHaveAccessibleLabel-test.js
@@ -1,3 +1,4 @@
-import expect from 'expect';
+import test from 'tape';
+
import mayHaveAccessibleLabel from '../../../src/util/mayHaveAccessibleLabel';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
@@ -8,69 +9,92 @@
import LiteralMock from '../../../__mocks__/LiteralMock';
-describe('mayHaveAccessibleLabel', () => {
- describe('no label', () => {
- it('should return false', () => {
- expect(mayHaveAccessibleLabel(
+test('mayHaveAccessibleLabel', (t) => {
+ t.equal(
+ mayHaveAccessibleLabel(
+ JSXElementMock('div', [], [
JSXElementMock('div', [], [
- JSXElementMock('div', [], [
+ JSXElementMock('span', [], []),
+ JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
- JSXElementMock('span', [], [
- JSXElementMock('span', [], []),
- ]),
]),
]),
- JSXElementMock('span', [], []),
- JSXElementMock('img', [
- JSXAttributeMock('src', 'some/path'),
- ]),
]),
- 5,
- )).toBe(false);
- });
- });
- describe('label via attributes', () => {
- it('aria-label, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ JSXElementMock('span', [], []),
+ JSXElementMock('img', [
+ JSXAttributeMock('src', 'some/path'),
+ ]),
+ ]),
+ 5,
+ ),
+ false,
+ 'no label returns false',
+ );
+
+ t.test('label via attributes', (st) => {
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', 'A delicate label'),
- ], []))).toBe(true);
- });
- it('aria-label without content, should return false', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ true,
+ 'aria-label returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', ''),
- ], []))).toBe(false);
- });
- it('aria-label with only whitespace, should return false', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ false,
+ 'aria-label without content returns false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', ' '),
- ], []))).toBe(false);
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ false,
+ 'aria-label with only spaces whitespace, should return false',
+ );
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', '\n'),
- ], []))).toBe(false);
- });
- it('aria-labelledby, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ false,
+ 'aria-label with only newline whitespace, should return false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', 'elementId'),
- ], []))).toBe(true);
- });
- it('aria-labelledby without content, should return false', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ true,
+ 'aria-labelledby returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', ''),
- ], []))).toBe(false);
- });
- it('aria-labelledby with an expression container, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
+ ], [])),
+ false,
+ 'aria-labelledby without content returns false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', 'elementId', true),
- ], []))).toBe(true);
- });
+ ], [])),
+ true,
+ 'aria-labelledby with an expression container, should return true',
+ );
+
+ st.end();
});
- describe('label via custom label attribute', () => {
- let customLabelProp;
- beforeEach(() => {
- customLabelProp = 'cowbell';
- });
- it('aria-label, should return true', () => {
- expect(mayHaveAccessibleLabel(
+
+ t.test('label via custom label attribute', (st) => {
+ const customLabelProp = 'cowbell';
+
+ st.equal(
+ mayHaveAccessibleLabel(
JSXElementMock('div', [
JSXAttributeMock(customLabelProp, 'A delicate label'),
@@ -78,35 +102,57 @@
1,
[customLabelProp],
- )).toBe(true);
- });
+ ),
+ true,
+ 'aria-label returns true',
+ );
+
+ st.end();
});
- describe('text label', () => {
- it('Literal text, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+
+ t.test('text label', (st) => {
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock('A fancy label'),
- ]))).toBe(true);
- });
- it('Literal whitespace, should return false', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ true,
+ 'Literal text, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock(' '),
- ]))).toBe(false);
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ false,
+ 'Literal spaces whitespace, returns false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock('\n'),
- ]))).toBe(false);
- });
- it('JSXText, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ false,
+ 'Literal newline whitespace, returns false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXTextMock('A fancy label'),
- ]))).toBe(true);
- });
- it('label is outside of default depth, should return false', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ true,
+ 'JSXText, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXTextMock('A fancy label'),
]),
- ]))).toBe(false);
- });
- it('label is inside of custom depth, should return true', () => {
- expect(mayHaveAccessibleLabel(
+ ])),
+ false,
+ 'label is outside of default depth, returns false',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
@@ -115,8 +161,11 @@
]),
2,
- )).toBe(true);
- });
- it('deep nesting, should return true', () => {
- expect(mayHaveAccessibleLabel(
+ ),
+ true,
+ 'label is inside of custom depth, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
@@ -139,48 +188,69 @@
]),
6,
- )).toBe(true);
- });
+ ),
+ true,
+ 'deep nesting, returns true',
+ );
+
+ st.end();
});
- describe('image content', () => {
- it('without alt, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+
+ t.test('image content', (st) => {
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
]),
- ]))).toBe(false);
- });
- it('with alt, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ false,
+ 'without alt, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
JSXAttributeMock('alt', 'A sensible label'),
]),
- ]))).toBe(true);
- });
- it('with aria-label, should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ ])),
+ true,
+ 'with alt, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
JSXAttributeMock('aria-label', 'A sensible label'),
]),
- ]))).toBe(true);
- });
+ ])),
+ true,
+ 'with aria-label, returns true',
+ );
+
+ st.end();
});
- describe('Intederminate situations', () => {
- describe('expression container children', () => {
- it('should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [], [
- JSXExpressionContainerMock('mysteryBox'),
- ]))).toBe(true);
- });
- });
- describe('spread operator in attributes', () => {
- it('should return true', () => {
- expect(mayHaveAccessibleLabel(JSXElementMock('div', [
- JSXAttributeMock('style', 'some-junk'),
- JSXSpreadAttributeMock('props'),
- ], []))).toBe(true);
- });
- });
+
+ t.test('Intederminate situations', (st) => {
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [], [
+ JSXExpressionContainerMock('mysteryBox'),
+ ])),
+ true,
+ 'expression container children, returns true',
+ );
+
+ st.equal(
+ mayHaveAccessibleLabel(JSXElementMock('div', [
+ JSXAttributeMock('style', 'some-junk'),
+ JSXSpreadAttributeMock('props'),
+ ], [])),
+ true,
+ 'spread operator in attributes, returns true',
+ );
+
+ st.end();
});
+
+ t.end();
});
diff --git a/lib/util/mayHaveAccessibleLabel.js b/lib/util/mayHaveAccessibleLabel.js
index v6.9.0..v6.10.0 100644
--- a/lib/util/mayHaveAccessibleLabel.js
+++ b/lib/util/mayHaveAccessibleLabel.js
@@ -7,4 +7,5 @@
var _arrayIncludes = _interopRequireDefault(require("array-includes"));
var _jsxAstUtils = require("jsx-ast-utils");
+var _minimatch = _interopRequireDefault(require("minimatch"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
/**
@@ -41,4 +42,6 @@
var maxDepth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var additionalLabellingProps = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
+ var getElementType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _jsxAstUtils.elementType;
+ var controlComponents = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
function checkElement(node, depth) {
// Bail when maxDepth is exceeded.
@@ -65,4 +68,15 @@
return true;
}
+ if (node.type === 'JSXElement' && node.children.length === 0 && node.openingElement) {
+ // $FlowFixMe `node.openingElement` has `unknown` type
+ var name = getElementType(node.openingElement);
+ var isReactComponent = name.length > 0 && name[0] === name[0].toUpperCase();
+ if (isReactComponent && !controlComponents.some(function (control) {
+ return (0, _minimatch["default"])(name, control);
+ })) {
+ return true;
+ }
+ }
+
// Recurse into the child element nodes.
if (node.children) {
diff --git a/__tests__/src/util/implicitRoles/menu-test.js b/__tests__/src/util/implicitRoles/menu-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/implicitRoles/menu-test.js
+++ b/__tests__/src/util/implicitRoles/menu-test.js
@@ -1,12 +1,20 @@
-import expect from 'expect';
+import test from 'tape';
+
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForMenu from '../../../../src/util/implicitRoles/menu';
-describe('isAbstractRole', () => {
- it('works for toolbars', () => {
- expect(getImplicitRoleForMenu([JSXAttributeMock('type', 'toolbar')])).toBe('toolbar');
- });
- it('works for non-toolbars', () => {
- expect(getImplicitRoleForMenu([JSXAttributeMock('type', '')])).toBe('');
- });
+test('isAbstractRole', (t) => {
+ t.equal(
+ getImplicitRoleForMenu([JSXAttributeMock('type', 'toolbar')]),
+ 'toolbar',
+ 'works for toolbars',
+ );
+
+ t.equal(
+ getImplicitRoleForMenu([JSXAttributeMock('type', '')]),
+ '',
+ 'works for non-toolbars',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/util/implicitRoles/menuitem-test.js b/__tests__/src/util/implicitRoles/menuitem-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/implicitRoles/menuitem-test.js
+++ b/__tests__/src/util/implicitRoles/menuitem-test.js
@@ -1,21 +1,38 @@
-import expect from 'expect';
+import test from 'tape';
+
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForMenuitem from '../../../../src/util/implicitRoles/menuitem';
-describe('isAbstractRole', () => {
- it('works for menu items', () => {
- expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'command')])).toBe('menuitem');
- });
- it('works for menu item checkboxes', () => {
- expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'checkbox')])).toBe('menuitemcheckbox');
- });
- it('works for menu item radios', () => {
- expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', 'radio')])).toBe('menuitemradio');
- });
- it('works for non-toolbars', () => {
- expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', '')])).toBe('');
- });
- it('works for the true case', () => {
- expect(getImplicitRoleForMenuitem([JSXAttributeMock('type', true)])).toBe('');
- });
+test('isAbstractRole', (t) => {
+ t.equal(
+ getImplicitRoleForMenuitem([JSXAttributeMock('type', 'command')]),
+ 'menuitem',
+ 'works for menu items',
+ );
+
+ t.equal(
+ getImplicitRoleForMenuitem([JSXAttributeMock('type', 'checkbox')]),
+ 'menuitemcheckbox',
+ 'works for menu item checkboxes',
+ );
+
+ t.equal(
+ getImplicitRoleForMenuitem([JSXAttributeMock('type', 'radio')]),
+ 'menuitemradio',
+ 'works for menu item radios',
+ );
+
+ t.equal(
+ getImplicitRoleForMenuitem([JSXAttributeMock('type', '')]),
+ '',
+ 'works for non-toolbars',
+ );
+
+ t.equal(
+ getImplicitRoleForMenuitem([JSXAttributeMock('type', true)]),
+ '',
+ 'works for the true case',
+ );
+
+ t.end();
});
diff --git a/__tests__/src/rules/no-noninteractive-element-interactions-test.js b/__tests__/src/rules/no-noninteractive-element-interactions-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/no-noninteractive-element-interactions-test.js
+++ b/__tests__/src/rules/no-noninteractive-element-interactions-test.js
@@ -337,5 +337,4 @@
{ code: '<thead onClick={() => {}} />;', errors: [expectedError] },
{ code: '<time onClick={() => {}} />;', errors: [expectedError] },
- { code: '<ol onClick={() => {}} />;', errors: [expectedError] },
{ code: '<ul onClick={() => {}} />;', errors: [expectedError] },
{ code: '<ul contentEditable="false" onClick={() => {}} />;', errors: [expectedError] },
@@ -346,5 +345,4 @@
{ code: '<div role="alertdialog" onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div role="application" onClick={() => {}} />;', errors: [expectedError] },
- { code: '<div role="article" onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div role="banner" onClick={() => {}} />;', errors: [expectedError] },
{ code: '<div role="cell" onClick={() => {}} />;', errors: [expectedError] },
diff --git a/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js b/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js
+++ b/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js
@@ -361,5 +361,4 @@
{ code: '<main role="button" />;', errors: [expectedError] },
{ code: '<article role="button" />;', errors: [expectedError] },
- { code: '<article role="button" />;', errors: [expectedError] },
{ code: '<aside role="button" />;', errors: [expectedError] },
{ code: '<blockquote role="button" />;', errors: [expectedError] },
@@ -413,5 +412,4 @@
{ code: '<main role="menuitem" />;', errors: [expectedError] },
{ code: '<article role="menuitem" />;', errors: [expectedError] },
- { code: '<article role="menuitem" />;', errors: [expectedError] },
{ code: '<dd role="menuitem" />;', errors: [expectedError] },
{ code: '<dfn role="menuitem" />;', errors: [expectedError] },
diff --git a/__tests__/src/rules/no-static-element-interactions-test.js b/__tests__/src/rules/no-static-element-interactions-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/no-static-element-interactions-test.js
+++ b/__tests__/src/rules/no-static-element-interactions-test.js
@@ -305,5 +305,4 @@
{ code: '<data onClick={() => {}} />;', errors: [expectedError] },
{ code: '<del onClick={() => {}} />;', errors: [expectedError] },
- { code: '<div onClick={() => {}} />;', errors: [expectedError] },
{ code: '<em onClick={() => {}} />;', errors: [expectedError] },
{ code: '<font onClick={() => {}} />;', errors: [expectedError] },
diff --git a/__tests__/src/util/parserOptionsMapper-test.js b/__tests__/src/util/parserOptionsMapper-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/parserOptionsMapper-test.js
+++ b/__tests__/src/util/parserOptionsMapper-test.js
@@ -1,16 +1,16 @@
-import expect from 'expect';
+import { version as eslintVersion } from 'eslint/package.json';
+import test from 'tape';
+import semver from 'semver';
+
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
-describe('parserOptionsMapper', () => {
- it('should return an test case object', () => {
- const testCase = {
+const usingLegacy = semver.major(eslintVersion) < 9;
+
+test('parserOptionsMapper', (t) => {
+ const expectedResult = usingLegacy
+ ? {
code: '<div />',
errors: [],
options: {},
- };
- expect(parserOptionsMapper(testCase)).toEqual({
- code: '<div />',
- errors: [],
- options: {},
parserOptions: {
ecmaVersion: 2018,
@@ -20,19 +20,37 @@
},
},
- });
- });
- it('should allow for overriding parserOptions', () => {
- const testCase = {
+ settings: {},
+ }
+ : {
code: '<div />',
errors: [],
options: {},
- parserOptions: {
- ecmaVersion: 5,
+ languageOptions: {
+ ecmaVersion: 'latest',
+ parserOptions: {
+ ecmaFeatures: {
+ experimentalObjectRestSpread: true,
+ jsx: true,
+ },
+ },
},
+ settings: {},
};
- expect(parserOptionsMapper(testCase)).toEqual({
+
+ t.deepEqual(
+ parserOptionsMapper({
code: '<div />',
errors: [],
options: {},
+ }),
+ expectedResult,
+ 'returns a test case object',
+ );
+
+ const expectedResult2 = usingLegacy
+ ? {
+ code: '<div />',
+ errors: [],
+ options: {},
parserOptions: {
ecmaVersion: 5,
@@ -42,5 +60,34 @@
},
},
- });
- });
+ settings: {},
+ }
+ : {
+ code: '<div />',
+ errors: [],
+ options: {},
+ languageOptions: {
+ ecmaVersion: 5,
+ parserOptions: {
+ ecmaFeatures: {
+ experimentalObjectRestSpread: true,
+ jsx: true,
+ },
+ },
+ },
+ settings: {},
+ };
+ t.deepEqual(
+ parserOptionsMapper({
+ code: '<div />',
+ errors: [],
+ options: {},
+ languageOptions: {
+ ecmaVersion: 5,
+ },
+ }),
+ expectedResult2,
+ 'allows for overriding parserOptions',
+ );
+
+ t.end();
});
diff --git a/__tests__/__util__/parserOptionsMapper.js b/__tests__/__util__/parserOptionsMapper.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/__util__/parserOptionsMapper.js
+++ b/__tests__/__util__/parserOptionsMapper.js
@@ -1,4 +1,8 @@
+import { version as eslintVersion } from 'eslint/package.json';
+import semver from 'semver';
+
+const usingLegacy = semver.major(eslintVersion) < 9;
+
const defaultParserOptions = {
- ecmaVersion: 2018,
ecmaFeatures: {
experimentalObjectRestSpread: true,
@@ -7,20 +11,43 @@
};
+const defaultLegacyParserOptions = {
+ ...defaultParserOptions,
+ ecmaVersion: 2018,
+};
+
+const defaultLanguageOptions = {
+ ecmaVersion: 'latest',
+ parserOptions: {
+ ...defaultParserOptions,
+ },
+};
+
export default function parserOptionsMapper({
code,
errors,
options = [],
- parserOptions = {},
- settings,
+ languageOptions = {},
+ settings = {},
}) {
- return {
- code,
- errors,
- options,
- parserOptions: {
- ...defaultParserOptions,
- ...parserOptions,
- },
- settings,
- };
+ return usingLegacy
+ ? {
+ code,
+ errors,
+ options,
+ parserOptions: {
+ ...defaultLegacyParserOptions,
+ ...languageOptions,
+ },
+ settings,
+ }
+ : {
+ code,
+ errors,
+ options,
+ languageOptions: {
+ ...defaultLanguageOptions,
+ ...languageOptions,
+ },
+ settings,
+ };
}
diff --git a/__tests__/src/rules/prefer-tag-over-role-test.js b/__tests__/src/rules/prefer-tag-over-role-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/rules/prefer-tag-over-role-test.js
+++ b/__tests__/src/rules/prefer-tag-over-role-test.js
@@ -11,5 +11,5 @@
});
-ruleTester.run('element-role', rule, {
+ruleTester.run('prefer-tag-over-role', rule, {
valid: parsers.all([].concat(
{ code: '<div />;' },
@@ -57,8 +57,4 @@
},
{
- code: '<other role="checkbox" />',
- errors: [expectedError('checkbox', '<input type="checkbox">')],
- },
- {
code: '<div role="banner" />',
errors: [expectedError('banner', '<header>')],
diff --git a/__tests__/src/util/schemas-test.js b/__tests__/src/util/schemas-test.js
index v6.9.0..v6.10.0 100644
--- a/__tests__/src/util/schemas-test.js
+++ b/__tests__/src/util/schemas-test.js
@@ -1,7 +1,8 @@
-import expect from 'expect';
+import test from 'tape';
+
import { generateObjSchema, arraySchema, enumArraySchema } from '../../../src/util/schemas';
-describe('schemas', () => {
- it('should generate an object schema with correct properties', () => {
+test('schemas', (t) => {
+ t.test('should generate an object schema with correct properties', (st) => {
const schema = generateObjSchema({
foo: 'bar',
@@ -10,20 +11,25 @@
const properties = schema.properties || {};
- expect(properties.foo).toEqual(properties.foo, 'bar');
- expect(properties.baz.type).toEqual('array');
+ st.deepEqual(properties.foo, properties.foo, 'bar');
+ st.deepEqual(properties.baz.type, 'array');
+
+ st.end();
});
- describe('enumArraySchema', () => {
- it('works with no arguments', () => {
- expect(enumArraySchema()).toEqual({
- additionalItems: false,
- items: {
- enum: [],
- type: 'string',
- },
- minItems: 0,
- type: 'array',
- uniqueItems: true,
- });
- });
- });
+
+ t.deepEqual(
+ enumArraySchema(),
+ {
+ additionalItems: false,
+ items: {
+ enum: [],
+ type: 'string',
+ },
+ minItems: 0,
+ type: 'array',
+ uniqueItems: true,
+ },
+ 'enumArraySchema works with no arguments',
+ );
+
+ t.end();
});
diff --git a/package.json b/package.json
index v6.9.0..v6.10.0 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
"name": "eslint-plugin-jsx-a11y",
- "version": "6.9.0",
+ "version": "6.10.0",
"description": "Static AST checker for accessibility rules on JSX elements.",
"keywords": [
@@ -23,11 +23,11 @@
"flow": "flow",
"lint:fix": "npm run lint -- --fix",
- "lint": "eslint --ext=js,mjs,cjs,ts,tsx .",
+ "lint": "npx eslint@8 --ext=js,mjs,cjs,ts,tsx .",
"prepublish": "not-in-publish || npm run prepublishOnly",
- "prepublishOnly": "safe-publish-latest && npm run lint && npm run flow && npm run jest",
+ "prepublishOnly": "safe-publish-latest && npm run lint && npm run flow && npm run tests-only",
"pretest": "npm run lint:fix && npm run flow",
- "test": "npm run jest",
- "posttest": "aud --production",
- "test:ci": "npm run jest -- --ci --runInBand",
+ "test": "npm run tests-only",
+ "tests-only": "tape --require=@babel/register '__tests__/**/*.js'",
+ "posttest": "npx npm@'>=10.2' audit --production",
"pretest:examples": "npm run build",
"test:examples": "npm run test-example:legacy && npm run test-example:flat-esm && npm run test-example:flat-cjs",
@@ -35,5 +35,4 @@
"test-example:flat-esm": "cd examples/flat-esm && npm install && npm run lint",
"test-example:flat-cjs": "cd examples/flat-cjs && npm install && npm run lint",
- "jest": "jest --coverage __tests__/**/*",
"pregenerate-list-of-rules": "npm run build",
"generate-list-of-rules": "eslint-doc-generator --rule-doc-title-format prefix-name --rule-doc-section-options false --config-emoji recommended,☑️ --ignore-config flat/recommended --ignore-config flat/strict",
@@ -43,15 +42,14 @@
},
"devDependencies": {
- "@babel/cli": "^7.24.7",
- "@babel/core": "^7.24.7",
- "@babel/eslint-parser": "^7.24.7",
- "@babel/plugin-transform-flow-strip-types": "^7.24.7",
+ "@babel/cli": "^7.25.6",
+ "@babel/core": "^7.25.2",
+ "@babel/eslint-parser": "^7.25.1",
+ "@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@babel/register": "^7.24.6",
- "aud": "^2.0.4",
"auto-changelog": "^2.4.0",
"babel-jest": "^24.9.0",
"babel-plugin-add-module-exports": "^1.0.4",
"babel-preset-airbnb": "^5.0.0",
- "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8",
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-doc-generator": "^1.7.1",
@@ -60,9 +58,7 @@
"eslint-plugin-import": "^2.29.1",
"estraverse": "^5.3.0",
- "expect": "^24.9.0",
"flow-bin": "^0.147.0",
"in-publish": "^2.0.1",
"jackspeak": "=2.1.1",
- "jest": "^24.9.0",
"jscodeshift": "^0.7.1",
"minimist": "^1.2.8",
@@ -73,4 +69,5 @@
"safe-publish-latest": "^2.0.0",
"semver": "^6.3.1",
+ "tape": "^5.8.1",
"to-ast": "^1.0.0"
},
@@ -84,6 +81,6 @@
"array.prototype.flatmap": "^1.3.2",
"ast-types-flow": "^0.0.8",
- "axe-core": "^4.9.1",
- "axobject-query": "~3.1.1",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
"damerau-levenshtein": "^1.0.8",
"emoji-regex": "^9.2.2",
@@ -98,21 +95,6 @@
},
"peerDependencies": {
- "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8"
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
},
- "jest": {
- "coverageReporters": [
- "lcov",
- "json",
- "html"
- ],
- "coverageDirectory": "coverage",
- "roots": [
- "__tests__"
- ],
- "testPathIgnorePatterns": [
- "__tests__/__util__/"
- ],
- "testEnvironment": "node"
- },
"auto-changelog": {
"output": "CHANGELOG.md",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index v6.9.0..v6.10.0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,4 +6,27 @@
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [v6.10.0](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/compare/v6.9.0...v6.10.0) - 2024-09-03
+
+### Fixed
+
+- [New] `label-has-associated-control`: add additional error message [`#1005`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/1005)
+- [Fix] `label-has-associated-control`: ignore undetermined label text [`#966`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/966)
+
+### Commits
+
+- [Tests] switch from jest to tape [`a284cbf`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/a284cbf4eb21292c4cff87f02be0bfb82764757f)
+- [New] add eslint 9 support [`deac4fd`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/deac4fd06eff4c0f5da27611c2a44a009b7e7fda)
+- [New] add `attributes` setting [`a1ee7f8`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/a1ee7f8810efafe416eb5d7f6eb0505b52873495)
+- [New] allow polymorphic linting to be restricted [`6cd1a70`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/6cd1a7011446e3925f2b49c51ff26246a21491d1)
+- [Tests] remove duplicate tests [`74d5dec`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/74d5decb6f2e42c05ce40a45630041fd695a2e7f)
+- [Dev Deps] update `@babel/cli`, `@babel/core`, `@babel/eslint-parser`, `@babel/plugin-transform-flow-strip-types` [`6eca235`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/6eca2359f5457af72dbfba265b73297c9232cb3e)
+- [readme] remove deprecated travis ci badge; add github actions badge [`0be7ea9`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/0be7ea95f560c6afc6817d381054d914ebd0b2ca)
+- [Tests] use `npm audit` instead of `aud` [`05a5e49`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/05a5e4992900e0d5d61e29e13046c90797b68a7c)
+- [Deps] update `axobject-query` [`912e98c`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/912e98c425ef9fcc2d7d22b45b4f7e3b445112a5)
+- [Deps] unpin `axobject-query` [`75147aa`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/75147aa68888fc150a4efea5b99809969bdc32b2)
+- [Deps] update `axe-core` [`27ff7cb`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/27ff7cbf562bf2685fd5a6062e58eb4727cb85c6)
+- [readme] fix jsxA11y import name [`ce846e0`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/ce846e00414c41676a6a8601022059878bcc0b89)
+- [readme] fix typo in shareable config section in readme [`cca288b`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/commit/cca288b73a39fa0932a57c02a7a88de68fc971fc)
+
## [v6.9.0](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/compare/v6.8.0...v6.9.0) - 2024-06-19
diff --git a/docs/rules/label-has-for.md b/docs/rules/label-has-for.md
index v6.9.0..v6.10.0 100644
--- a/docs/rules/label-has-for.md
+++ b/docs/rules/label-has-for.md
@@ -14,5 +14,5 @@
- nesting: by wrapping a control in a label tag
-- id: by using the prop `htmlFor` as in `htmlFor=[ID of control]`
+- id: by using the prop `htmlFor` (or any configured attribute) as in `htmlFor=[ID of control]`
To fully cover 100% of assistive devices, you're encouraged to validate for both nesting and id.
diff --git a/README.md b/README.md
index v6.9.0..v6.10.0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
<p align="center">
- <a href="https://travis-ci.com/github/jsx-eslint/eslint-plugin-jsx-a11y">
- <img src="https://travis-ci.com/jsx-eslint/eslint-plugin-jsx-a11y.svg?branch=master"
- alt="build status">
+ <a href="https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/actions">
+ <img src="https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/jsx-eslint/eslint-plugin-jsx-a11y"
+ alt="CI status" />
</a>
<a href="https://npmjs.org/package/eslint-plugin-jsx-a11y">
@@ -105,4 +105,7 @@
"MyButton": "button",
"RoundButton": "button"
+ },
+ "attributes": {
+ "for": ["htmlFor", "for"]
}
}
@@ -175,9 +178,9 @@
```
-**Note**: Our shareable config do configure `files` or [`languageOptions.globals`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects).
+**Note**: Our shareable configs do NOT configure `files` or [`languageOptions.globals`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects).
For most of the cases, you probably want to configure some of these properties yourself.
```js
-const jsxA11yRecommended = require('eslint-plugin-jsx-a11y');
+const jsxA11y = require('eslint-plugin-jsx-a11y');
const globals = require('globals');
@@ -203,4 +206,9 @@
To enable your custom components to be checked as DOM elements, you can set global settings in your configuration file by mapping each custom component name to a DOM element type.
+#### Attribute Mapping
+
+To configure the JSX property to use for attribute checking, you can set global settings in your configuration file by mapping each DOM attribute to the JSX property you want to check.
+For example, you may want to allow the `for` attribute in addition to the `htmlFor` attribute for checking label associations.
+
#### Polymorphic Components
@@ -214,4 +222,6 @@
will be evaluated as an `h3`. If no `polymorphicPropName` is set, then the component will be evaluated as `Box`.
+To restrict polymorphic linting to specified components, additionally set `polymorphicAllowList` to an array of component names.
+
⚠️ Polymorphic components can make code harder to maintain; please use this feature with caution.
Command detailsnpm diff --diff=eslint-plugin-jsx-a11y@6.9.0 --diff=eslint-plugin-jsx-a11y@6.10.0 --diff-unified=2 See also the Reported by ybiquitous/npm-diff-action@v1.6.0 (Node.js 22.9.0 and npm 10.8.3) |
github-actions
bot
deleted the
dependabot/npm_and_yarn/eslint-plugin-jsx-a11y-6.10.0
branch
October 1, 2024 03:20
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
dependencies
Pull requests that update a dependency file
javascript
Pull requests that update Javascript code
0 participants
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Bumps eslint-plugin-jsx-a11y from 6.9.0 to 6.10.0.
Release notes
Sourced from eslint-plugin-jsx-a11y's releases.
Changelog
Sourced from eslint-plugin-jsx-a11y's changelog.
Commits
65c9338
v6.10.0912e98c
[Deps] updateaxobject-query
6cd1a70
[New] allow polymorphic linting to be restricteda1ee7f8
[New] addattributes
setting83fd9c4
[New]label-has-associated-control
: add additional error message75147aa
[Deps] unpinaxobject-query
a284cbf
[Tests] switch from jest to tapedeac4fd
[New] add eslint 9 support74d5dec
[Tests] remove duplicate tests05a5e49
[Tests] usenpm audit
instead ofaud
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase
.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebase
will rebase this PR@dependabot recreate
will recreate this PR, overwriting any edits that have been made to it@dependabot merge
will merge this PR after your CI passes on it@dependabot squash and merge
will squash and merge this PR after your CI passes on it@dependabot cancel merge
will cancel a previously requested merge and block automerging@dependabot reopen
will reopen this PR if it is closed@dependabot close
will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditions
will show all of the ignore conditions of the specified dependency@dependabot ignore this major version
will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor version
will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependency
will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)