inital commit

This commit is contained in:
2026-01-01 15:25:19 +05:30
commit f0ae49465a
36361 changed files with 4894111 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
import test from 'tape';
import attributesComparator from '../../../src/util/attributesComparator';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
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();
});

View File

@@ -0,0 +1,174 @@
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';
test('getAccessibleChildText', (t) => {
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-label', 'foo')],
), elementType),
'foo',
'returns the aria-label when present',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-label', 'foo')],
[{ type: 'JSXText', value: 'bar' }],
), elementType),
'foo',
'returns the aria-label instead of children',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[JSXAttributeMock('aria-hidden', 'true')],
), elementType),
'',
'skips elements with aria-hidden=true',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'JSXText', value: 'bar' }],
), elementType),
'bar',
'returns literal value for JSXText child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
JSXAttributeMock('alt', 'a sensible label'),
])],
), elementType),
'a sensible label',
'returns alt text for img child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[JSXElementMock('span', [
JSXAttributeMock('alt', 'a sensible label'),
])],
), elementType),
'',
'returns blank when alt tag is used on arbitrary element',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'bar' }],
), elementType),
'bar',
'returns literal value for JSXText child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: ' bar ' }],
), elementType),
'bar',
'returns trimmed literal value for JSXText child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo bar' }],
), elementType),
'foo bar',
'returns space-collapsed literal value for JSXText child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo, bar. baz? foo; bar:' }],
), elementType),
'foo bar baz foo bar',
'returns punctuation-stripped literal value for JSXText child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[JSXElementMock(
'span',
[],
[{ type: 'Literal', value: 'bar' }],
)],
), elementType),
'bar',
'returns recursive value for JSXElement child',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[JSXElementMock(
'span',
[],
[JSXElementMock(
'span',
[JSXAttributeMock('aria-hidden', 'true')],
)],
)],
), elementType),
'',
'skips children with aria-hidden-true',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo' }, { type: 'Literal', value: 'bar' }],
), elementType),
'foo bar',
'joins multiple children properly - no spacing',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: ' foo ' }, { type: 'Literal', value: ' bar ' }],
), elementType),
'foo bar',
'joins multiple children properly - with spacing',
);
t.equal(
getAccessibleChildText(JSXElementMock(
'a',
[],
[{ type: 'Literal', value: 'foo' }, { type: 'Unknown' }, { type: 'Literal', value: 'bar' }],
), elementType),
'foo bar',
'skips unknown elements',
);
t.end();
});

View File

@@ -0,0 +1,71 @@
import test from 'tape';
import getComputedRole from '../../../src/util/getComputedRole';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
test('getComputedRole', (t) => {
t.equal(
getComputedRole(
'div',
[JSXAttributeMock('role', 'button')],
),
'button',
'explicit role + valid role -> returns the role',
);
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();
});

View File

@@ -0,0 +1,154 @@
import test from 'tape';
import getElementType from '../../../src/util/getElementType';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
test('getElementType', (t) => {
t.test('no settings in context', (st) => {
const elementType = getElementType({ settings: {} });
st.equal(
elementType(JSXElementMock('input').openingElement),
'input',
'returns the exact tag name for a DOM element',
);
st.equal(
elementType(JSXElementMock('CustomInput').openingElement),
'CustomInput',
'returns the exact tag name for a custom element',
);
st.equal(
elementType(JSXElementMock('toString').openingElement),
'toString',
'returns the exact tag name for names that are in Object.prototype',
);
st.equal(
elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
'span',
'returns the default tag name provided',
);
st.end();
});
t.test('components settings in context', (st) => {
const elementType = getElementType({
settings: {
'jsx-a11y': {
components: {
CustomInput: 'input',
},
},
},
});
st.equal(
elementType(JSXElementMock('input').openingElement),
'input',
'returns the exact tag name for a DOM element',
);
st.equal(
elementType(JSXElementMock('CustomInput').openingElement),
'input',
'returns the mapped tag name for a custom element',
);
st.equal(
elementType(JSXElementMock('CityInput').openingElement),
'CityInput',
'returns the exact tag name for a custom element not in the components map',
);
st.equal(
elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
'span',
'return the default tag name since not polymorphicPropName was provided',
);
st.end();
});
t.test('polymorphicPropName settings in context', (st) => {
const elementType = getElementType({
settings: {
'jsx-a11y': {
polymorphicPropName: 'asChild',
components: {
CustomButton: 'button',
},
},
},
});
st.equal(
elementType(JSXElementMock('span', [JSXAttributeMock('asChild', 'h1')]).openingElement),
'h1',
'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings',
);
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',
},
},
},
});
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();
});

View File

@@ -0,0 +1,35 @@
import test from 'tape';
import getExplicitRole from '../../../src/util/getExplicitRole';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
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();
});

View File

@@ -0,0 +1,25 @@
import test from 'tape';
import getImplicitRole from '../../../src/util/getImplicitRole';
test('getImplicitRole', (t) => {
t.equal(
getImplicitRole(
'li',
[],
),
'listitem',
'has implicit, returns implicit role',
);
t.equal(
getImplicitRole(
'div',
[],
),
null,
'lacks implicit, returns null',
);
t.end();
});

View File

@@ -0,0 +1,33 @@
import test from 'tape';
import getSuggestion from '../../../src/util/getSuggestion';
test('spell check suggestion API', (t) => {
t.deepEqual([], getSuggestion('foo'), 'returns no suggestions given empty word and no dictionary');
t.deepEqual(
getSuggestion('foo'),
[],
'returns no suggestions given real word and no dictionary',
);
t.deepEqual(
getSuggestion('fo', ['foo', 'bar', 'baz']),
['foo'],
'returns correct suggestion given real word and a dictionary',
);
t.deepEqual(
getSuggestion('theer', ['there', 'their', 'foo', 'bar']),
['there', 'their'],
'returns multiple correct suggestions given real word and a dictionary',
);
t.deepEqual(
getSuggestion('theer', ['there', 'their', 'foo', 'bar'], 1),
['there'],
'returns correct # of suggestions given the limit argument',
);
t.end();
});

View File

@@ -0,0 +1,85 @@
import test from 'tape';
import getTabIndex from '../../../src/util/getTabIndex';
import IdentifierMock from '../../../__mocks__/IdentifierMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
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();
});

View File

@@ -0,0 +1,157 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import hasAccessibleChild from '../../../src/util/hasAccessibleChild';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
test('hasAccessibleChild', (t) => {
t.equal(
hasAccessibleChild(JSXElementMock('div', []), elementType),
false,
'has no children and does not set dangerouslySetInnerHTML -> false',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [JSXAttributeMock('dangerouslySetInnerHTML', true)], []),
elementType,
),
true,
'has no children and sets dangerouslySetInnerHTML -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock(
'div',
[],
[{
type: 'Literal',
value: 'foo',
}],
),
elementType,
),
true,
'has children + Literal child -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [JSXElementMock('div', [])]),
elementType,
),
true,
'has children + visible JSXElement child -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [{
type: 'JSXText',
value: 'foo',
}]),
elementType,
),
true,
'has children + JSText element -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXElementMock('div', [
JSXAttributeMock('aria-hidden', true),
]),
]),
elementType,
),
false,
'has children + hidden child JSXElement -> false',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXExpressionContainerMock({
type: 'Identifier',
name: 'foo',
}),
]),
elementType,
),
true,
'defined JSXExpressionContainer -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXExpressionContainerMock({
type: 'Identifier',
name: 'undefined',
}),
]),
elementType,
),
false,
'has children + undefined JSXExpressionContainer -> false',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [{
type: 'Unknown',
}]),
elementType,
),
false,
'unknown child type -> false',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [JSXAttributeMock('children', true)], []),
elementType,
),
true,
'children passed as a prop -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXElementMock('input', [JSXAttributeMock('type', 'hidden')]),
]),
elementType,
),
false,
'has chidren -> hidden child input JSXElement -> false',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]),
]),
elementType,
),
true,
'has children + custom JSXElement of type hidden -> true',
);
t.equal(
hasAccessibleChild(
JSXElementMock('div', [], [
JSXElementMock('CustomInput', [JSXAttributeMock('type', 'hidden')]),
]),
() => 'input',
),
false,
'custom JSXElement mapped to input if type is hidden -> false',
);
t.end();
});

View File

@@ -0,0 +1,87 @@
import test from 'tape';
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForInput from '../../../../src/util/implicitRoles/input';
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();
});
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();
});
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();
});

View File

@@ -0,0 +1,20 @@
import test from 'tape';
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForMenu from '../../../../src/util/implicitRoles/menu';
test('isAbstractRole', (t) => {
t.equal(
getImplicitRoleForMenu([JSXAttributeMock('type', 'toolbar')]),
'toolbar',
'works for toolbars',
);
t.equal(
getImplicitRoleForMenu([JSXAttributeMock('type', '')]),
'',
'works for non-toolbars',
);
t.end();
});

View File

@@ -0,0 +1,38 @@
import test from 'tape';
import JSXAttributeMock from '../../../../__mocks__/JSXAttributeMock';
import getImplicitRoleForMenuitem from '../../../../src/util/implicitRoles/menuitem';
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();
});

View File

@@ -0,0 +1,51 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isAbstractRole from '../../../src/util/isAbstractRole';
import {
genElementSymbol,
genAbstractRoleElements,
genNonAbstractRoleElements,
} from '../../../__mocks__/genInteractives';
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;
st.equal(
isAbstractRole(
elementType(openingElement),
attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as an abstract role element`,
);
});
st.end();
});
t.test('elements with a non-abstract role', (st) => {
genNonAbstractRoleElements().forEach(({ openingElement }) => {
const { attributes } = openingElement;
st.equal(
isAbstractRole(
elementType(openingElement),
attributes,
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` as an abstract role element`,
);
});
st.end();
});
t.end();
});

View File

@@ -0,0 +1,52 @@
import test from 'tape';
import isContentEditable from '../../../src/util/isContentEditable';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
test('isContentEditable - HTML5', (t) => {
t.equal(
isContentEditable('some tag', [
JSXAttributeMock('contentEditable', 'true'),
]),
true,
'identifies HTML5 contentEditable elements',
);
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',
);
st.equal(
isContentEditable('some tag', [
JSXAttributeMock('contentEditable', undefined),
]),
false,
'does not identify HTML5 content editable elements with undefined as the value',
);
st.equal(
isContentEditable('some tag', [
JSXAttributeMock('contentEditable', true),
]),
false,
'does not identify HTML5 content editable elements with true as the value',
);
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();
});

View File

@@ -0,0 +1,30 @@
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';
test('isDOMElement', (t) => {
t.test('DOM elements', (st) => {
dom.forEach((_, el) => {
const element = JSXElementMock(el);
st.equal(
isDOMElement(elementType(element.openingElement)),
true,
`identifies ${el} as a DOM element`,
);
});
st.end();
});
t.equal(
isDOMElement(JSXElementMock('CustomElement')),
false,
'does not identify a custom element',
);
t.end();
});

View File

@@ -0,0 +1,88 @@
import test from 'tape';
import isDisabledElement from '../../../src/util/isDisabledElement';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
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();
});
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();
});

View File

@@ -0,0 +1,111 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isFocusable from '../../../src/util/isFocusable';
import {
genElementSymbol,
genInteractiveElements,
genNonInteractiveElements,
} from '../../../__mocks__/genInteractives';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
function mergeTabIndex(index, attributes) {
return [].concat(attributes, JSXAttributeMock('tabIndex', index));
}
test('isFocusable', (t) => {
t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isFocusable(
elementType(openingElement),
openingElement.attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
),
true,
`identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
),
true,
`identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`,
);
});
st.end();
});
t.test('non-interactive elements', (st) => {
genNonInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isFocusable(
elementType(openingElement),
openingElement.attributes,
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(-1, openingElement.attributes),
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of -1 as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(0, openingElement.attributes),
),
true,
`identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 0 as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex(1, openingElement.attributes),
),
true,
`identifies \`${genElementSymbol(openingElement)}\` with tabIndex of 1 as a focusable element`,
);
st.equal(
isFocusable(
elementType(openingElement),
mergeTabIndex('bogus', openingElement.attributes),
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` with tabIndex of 'bogus' as a focusable element`,
);
});
st.end();
});
t.end();
});

View File

@@ -0,0 +1,104 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isInteractiveElement from '../../../src/util/isInteractiveElement';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import {
genElementSymbol,
genIndeterminantInteractiveElements,
genInteractiveElements,
genInteractiveRoleElements,
genNonInteractiveElements,
genNonInteractiveRoleElements,
} from '../../../__mocks__/genInteractives';
test('isInteractiveElement', (t) => {
t.equal(
isInteractiveElement(undefined, []),
false,
'identifies them as interactive elements',
);
t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
);
});
st.end();
});
t.test('interactive role elements', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
st.equal(
isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
);
});
st.end();
});
t.test('non-interactive elements', (st) => {
genNonInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
);
});
st.end();
});
t.test('non-interactive role elements', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
st.equal(
isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
);
});
st.end();
});
t.test('indeterminate elements', (st) => {
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive element`,
);
});
st.end();
});
t.equal(
isInteractiveElement('CustomComponent', JSXElementMock()),
false,
'JSX elements are not interactive',
);
t.end();
});

View File

@@ -0,0 +1,59 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isInteractiveRole from '../../../src/util/isInteractiveRole';
import {
genElementSymbol,
genInteractiveRoleElements,
genNonInteractiveRoleElements,
} from '../../../__mocks__/genInteractives';
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;
st.equal(
isInteractiveRole(
elementType(openingElement),
attributes,
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` as an interactive role element`,
);
});
st.end();
});
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;
st.equal(
isInteractiveRole(
elementType(openingElement),
attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as an interactive role element`,
);
});
st.end();
});
t.end();
});

View File

@@ -0,0 +1,97 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isNonInteractiveElement from '../../../src/util/isNonInteractiveElement';
import {
genElementSymbol,
genIndeterminantInteractiveElements,
genInteractiveElements,
genInteractiveRoleElements,
genNonInteractiveElements,
genNonInteractiveRoleElements,
} from '../../../__mocks__/genInteractives';
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 }) => {
st.equal(
isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
);
});
st.end();
});
t.test('non-interactive role elements', (st) => {
genNonInteractiveRoleElements().forEach(({ openingElement }) => {
st.equal(
isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
);
});
st.end();
});
t.test('interactive elements', (st) => {
genInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
);
});
st.end();
});
t.test('interactive role elements', (st) => {
genInteractiveRoleElements().forEach(({ openingElement }) => {
st.equal(
isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
);
});
st.end();
});
t.test('indeterminate elements', (st) => {
genIndeterminantInteractiveElements().forEach(({ openingElement }) => {
st.equal(
isNonInteractiveElement(
elementType(openingElement),
openingElement.attributes,
),
false,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive element`,
);
});
st.end();
});
t.end();
});

View File

@@ -0,0 +1,59 @@
import test from 'tape';
import { elementType } from 'jsx-ast-utils';
import isNonInteractiveRole from '../../../src/util/isNonInteractiveRole';
import {
genElementSymbol,
genInteractiveRoleElements,
genNonInteractiveRoleElements,
} from '../../../__mocks__/genInteractives';
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;
st.equal(
isNonInteractiveRole(
elementType(openingElement),
attributes,
),
true,
`identifies \`${genElementSymbol(openingElement)}\` as a non-interactive role element`,
);
});
st.end();
});
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;
st.equal(
isNonInteractiveRole(
elementType(openingElement),
attributes,
),
false,
`does NOT identify \`${genElementSymbol(openingElement)}\` as a non-interactive role element`,
);
});
st.end();
});
t.end();
});

View File

@@ -0,0 +1,52 @@
import test from 'tape';
import isNonLiteralProperty from '../../../src/util/isNonLiteralProperty';
import IdentifierMock from '../../../__mocks__/IdentifierMock';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXSpreadAttributeMock from '../../../__mocks__/JSXSpreadAttributeMock';
import JSXTextMock from '../../../__mocks__/JSXTextMock';
import LiteralMock from '../../../__mocks__/LiteralMock';
const theProp = 'theProp';
const spread = JSXSpreadAttributeMock('theSpread');
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();
});

View File

@@ -0,0 +1,72 @@
import test from 'tape';
import isSemanticRoleElement from '../../../src/util/isSemanticRoleElement';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
test('isSemanticRoleElement', (t) => {
t.equal(
isSemanticRoleElement('input', [
JSXAttributeMock('type', 'checkbox'),
JSXAttributeMock('role', 'switch'),
]),
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();
});
t.doesNotThrow(
() => {
isSemanticRoleElement('input', [
JSXAttributeMock('type', 'checkbox'),
JSXAttributeMock('role', 'checkbox'),
JSXAttributeMock('aria-checked', 'false'),
JSXAttributeMock('aria-labelledby', 'foo'),
JSXAttributeMock('tabindex', '0'),
{
type: 'JSXSpreadAttribute',
argument: {
type: 'Identifier',
name: 'props',
},
},
]);
},
'does not throw on JSXSpreadAttribute',
);
t.end();
});

View File

@@ -0,0 +1,219 @@
import test from 'tape';
import mayContainChildComponent from '../../../src/util/mayContainChildComponent';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
test('mayContainChildComponent', (t) => {
t.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
]),
]),
]),
JSXElementMock('span', [], []),
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
]),
]),
'FancyComponent',
5,
),
false,
'no FancyComponent returns false',
);
t.test('contains an indicated component', (st) => {
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('input'),
]),
'input',
),
true,
'returns true',
);
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'FancyComponent',
),
true,
'returns true',
);
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
]),
'FancyComponent',
),
false,
'FancyComponent is outside of default depth, should return false',
);
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
]),
'FancyComponent',
2,
),
true,
'FancyComponent is inside of custom depth, should return true',
);
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], [
JSXElementMock('span', [], [
JSXElementMock('FancyComponent'),
]),
]),
]),
]),
]),
JSXElementMock('span', [], []),
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
]),
]),
'FancyComponent',
6,
),
true,
'deep nesting, returns true',
);
st.end();
});
t.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXExpressionContainerMock('mysteryBox'),
]),
'FancyComponent',
),
true,
'Intederminate situations + expression container children - returns true',
);
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*',
),
true,
'returns true',
);
s2t.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'*Component',
),
true,
'returns true',
);
s2t.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('FancyComponent'),
]),
'Fancy*C*t',
),
true,
'returns true',
);
s2t.end();
});
st.end();
});
t.test('using a custom elementType function', (st) => {
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('CustomInput'),
]),
'input',
2,
() => 'input',
),
true,
'returns true when the custom elementType returns the proper name',
);
st.equal(
mayContainChildComponent(
JSXElementMock('div', [], [
JSXElementMock('CustomInput'),
]),
'input',
2,
() => 'button',
),
false,
'returns false when the custom elementType returns a wrong name',
);
st.end();
});
t.end();
});

View File

@@ -0,0 +1,256 @@
import test from 'tape';
import mayHaveAccessibleLabel from '../../../src/util/mayHaveAccessibleLabel';
import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
import JSXElementMock from '../../../__mocks__/JSXElementMock';
import JSXExpressionContainerMock from '../../../__mocks__/JSXExpressionContainerMock';
import JSXSpreadAttributeMock from '../../../__mocks__/JSXSpreadAttributeMock';
import JSXTextMock from '../../../__mocks__/JSXTextMock';
import LiteralMock from '../../../__mocks__/LiteralMock';
test('mayHaveAccessibleLabel', (t) => {
t.equal(
mayHaveAccessibleLabel(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
]),
]),
]),
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'),
], [])),
true,
'aria-label returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', ''),
], [])),
false,
'aria-label without content returns false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', ' '),
], [])),
false,
'aria-label with only spaces whitespace, should return false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-label', '\n'),
], [])),
false,
'aria-label with only newline whitespace, should return false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', 'elementId'),
], [])),
true,
'aria-labelledby returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', ''),
], [])),
false,
'aria-labelledby without content returns false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [
JSXAttributeMock('aria-labelledby', 'elementId', true),
], [])),
true,
'aria-labelledby with an expression container, should return true',
);
st.end();
});
t.test('label via custom label attribute', (st) => {
const customLabelProp = 'cowbell';
st.equal(
mayHaveAccessibleLabel(
JSXElementMock('div', [
JSXAttributeMock(customLabelProp, 'A delicate label'),
], []),
1,
[customLabelProp],
),
true,
'aria-label returns true',
);
st.end();
});
t.test('text label', (st) => {
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock('A fancy label'),
])),
true,
'Literal text, returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock(' '),
])),
false,
'Literal spaces whitespace, returns false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
LiteralMock('\n'),
])),
false,
'Literal newline whitespace, returns false',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXTextMock('A fancy label'),
])),
true,
'JSXText, returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXTextMock('A fancy label'),
]),
])),
false,
'label is outside of default depth, returns false',
);
st.equal(
mayHaveAccessibleLabel(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXTextMock('A fancy label'),
]),
]),
2,
),
true,
'label is inside of custom depth, returns true',
);
st.equal(
mayHaveAccessibleLabel(
JSXElementMock('div', [], [
JSXElementMock('div', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], []),
JSXElementMock('span', [], [
JSXElementMock('span', [], [
JSXElementMock('span', [], [
JSXTextMock('A fancy label'),
]),
]),
]),
]),
]),
JSXElementMock('span', [], []),
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
]),
]),
6,
),
true,
'deep nesting, returns true',
);
st.end();
});
t.test('image content', (st) => {
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
]),
])),
false,
'without alt, returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
JSXAttributeMock('alt', 'A sensible label'),
]),
])),
true,
'with alt, returns true',
);
st.equal(
mayHaveAccessibleLabel(JSXElementMock('div', [], [
JSXElementMock('img', [
JSXAttributeMock('src', 'some/path'),
JSXAttributeMock('aria-label', 'A sensible label'),
]),
])),
true,
'with aria-label, returns true',
);
st.end();
});
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();
});

View File

@@ -0,0 +1,93 @@
import { version as eslintVersion } from 'eslint/package.json';
import test from 'tape';
import semver from 'semver';
import parserOptionsMapper from '../../__util__/parserOptionsMapper';
const usingLegacy = semver.major(eslintVersion) < 9;
test('parserOptionsMapper', (t) => {
const expectedResult = usingLegacy
? {
code: '<div />',
errors: [],
options: {},
parserOptions: {
ecmaVersion: 2018,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
},
settings: {},
}
: {
code: '<div />',
errors: [],
options: {},
languageOptions: {
ecmaVersion: 'latest',
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
},
},
settings: {},
};
t.deepEqual(
parserOptionsMapper({
code: '<div />',
errors: [],
options: {},
}),
expectedResult,
'returns a test case object',
);
const expectedResult2 = usingLegacy
? {
code: '<div />',
errors: [],
options: {},
parserOptions: {
ecmaVersion: 5,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
},
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();
});

View File

@@ -0,0 +1,35 @@
import test from 'tape';
import { generateObjSchema, arraySchema, enumArraySchema } from '../../../src/util/schemas';
test('schemas', (t) => {
t.test('should generate an object schema with correct properties', (st) => {
const schema = generateObjSchema({
foo: 'bar',
baz: arraySchema,
});
const properties = schema.properties || {};
st.deepEqual(properties.foo, properties.foo, 'bar');
st.deepEqual(properties.baz.type, 'array');
st.end();
});
t.deepEqual(
enumArraySchema(),
{
additionalItems: false,
items: {
enum: [],
type: 'string',
},
minItems: 0,
type: 'array',
uniqueItems: true,
},
'enumArraySchema works with no arguments',
);
t.end();
});