Skip to content
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

refactor(aria-text): use virtualNode #3581

Merged
merged 17 commits into from
Aug 10, 2022
62 changes: 32 additions & 30 deletions test/checks/keyboard/no-focusable-content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
describe('no-focusable-content tests', function() {
var fixture = document.querySelector('#fixture');
describe('no-focusable-content tests', function () {
var queryFixture = axe.testUtils.queryFixture;
var noFocusableContent = axe.testUtils.getCheckEvaluate(
'no-focusable-content'
Expand All @@ -8,52 +7,51 @@ describe('no-focusable-content tests', function() {
var check = checks['no-focusable-content'];
var checkContext = new axe.testUtils.MockCheckContext();

afterEach(function() {
fixture.innerHTML = '';
afterEach(function () {
checkContext.reset();
});

it('should return true if element has no focusable content', function() {
it('should return true if element has no focusable content', function () {
var vNode = queryFixture('<button id="target"><span>Hello</span></button>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true if element is empty', function() {
it('should return true if element is empty', function () {
var vNode = queryFixture('<button id="target"></button>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true if element only has text content', function() {
it('should return true if element only has text content', function () {
var vNode = queryFixture('<button id="target">Hello</button>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true if element has content which is focusable (tabindex=0) and does not have a widget role', function() {
it('should return true if element has content which is focusable (tabindex=0) and does not have a widget role', function () {
var params = checkSetup(
'<button id="target"><span tabindex="0">Hello</span></button>'
);

assert.isTrue(noFocusableContent.apply(checkContext, params));
});

it('should return true if element has content which has negative tabindex and non-widget role', function() {
it('should return true if element has content which has negative tabindex and non-widget role', function () {
var vNode = queryFixture(
'<button id="target"><span tabindex="-1">Hello</span></button>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return false if element has content which has negative tabindex and an explicit widget role', function() {
it('should return false if element has content which has negative tabindex and an explicit widget role', function () {
var params = checkSetup(
'<button id="target"><span role="link" tabindex="-1">Hello</span></button>'
);
axe.utils.getFlattenedTree(document.documentElement);

assert.isFalse(check.evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });
assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});

it('should return false if element has content which is natively focusable and has a widget role', function() {
it('should return false if element has content which is natively focusable and has a widget role', function () {
var params = checkSetup(
'<button id="target"><a href="foo.html">Hello</a></button>'
);
Expand All @@ -63,7 +61,7 @@ describe('no-focusable-content tests', function() {
assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});

it('should add each focusable child as related nodes', function() {
it('should add each focusable child as related nodes', function () {
var params = checkSetup(
'<button id="target"><input type="checkbox"><a href="foo.html">Hello</a></button>'
);
Expand All @@ -76,49 +74,53 @@ describe('no-focusable-content tests', function() {
]);
});

it('should return false if element has natively focusable widget role content with negative tabindex', function() {
it('should return false if element has natively focusable widget role content with negative tabindex', function () {
var params = checkSetup(
'<button id="target"><a href="foo.html" tabindex="-1">Hello</a></button>'
);
axe.utils.getFlattenedTree(document.documentElement);
assert.isFalse(check.evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });
assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});

it('should return true if element has content which is natively focusable and has a widget role but is disabled', function() {
it('should return true if element has content which is natively focusable and has a widget role but is disabled', function () {
var vNode = queryFixture(
'<button id="target"><input value="hello" disabled></button>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return false if "disabled" is specified on an element which doesn\'t allow it', function() {
it('should return false if "disabled" is specified on an element which doesn\'t allow it', function () {
var params = checkSetup(
'<button id="target"><a href="foo.html" disabled>Hello</a></button>'
);
assert.isFalse(noFocusableContent.apply(checkContext, params));
});

it('should return true on span with negative tabindex (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="-1">JavaScript is able to focus this</span> '
+'</span>');
it('should return true on span with negative tabindex (focusable, does not have a widget role)', function () {
var vNode = queryFixture(
'<span id="target" role="text"> some text ' +
'<span tabIndex="-1">JavaScript is able to focus this</span> ' +
'</span>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true on aria-hidden span with negative tabindex (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="-1" aria-hidden="true">JavaScript is able to focus this</span> '
+'</span>');
it('should return true on aria-hidden span with negative tabindex (focusable, does not have a widget role)', function () {
var vNode = queryFixture(
'<span id="target" role="text"> some text ' +
'<span tabIndex="-1" aria-hidden="true">JavaScript is able to focus this</span> ' +
'</span>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true on nested span with tabindex=0 (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="0">anyone is able to focus this</span> '
+'</span>');
it('should return true on nested span with tabindex=0 (focusable, does not have a widget role)', function () {
var vNode = queryFixture(
'<span id="target" role="text"> some text ' +
'<span tabIndex="0">anyone is able to focus this</span> ' +
'</span>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

});
198 changes: 198 additions & 0 deletions test/integration/virtual-rules/aria-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
describe('aria-text virtual-rule', function () {
it('should incomplete for element with undefined children', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 1);
});

it('should fail for focusable widget children', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'button',
attributes: {
role: 'widget'
}
dbowling marked this conversation as resolved.
Show resolved Hide resolved
});
node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for children with native focus', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'button'
});
node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should pass if element only has descendants that are not focusable', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'span'
});
var grandchild = new axe.SerialVirtualNode({
nodeName: '#text',
nodeType: 3,
nodeValue: 'hello'
});

child.children = [grandchild];
node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for deeply nested focusable children', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'div'
});
var grandchild = new axe.SerialVirtualNode({
nodeName: 'div'
});
var greatgrandchild = new axe.SerialVirtualNode({
nodeName: 'button'
});
greatgrandchild.parent = grandchild;
grandchild.children = [greatgrandchild];
child.children = [grandchild];
node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should fail when tabIndex is negative', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'a',
attributes: {
tabindex: '-1',
href: '#'
}
});

node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});

it('should pass when an anchor has no href and has implicit role of link', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'a',
attributes: {
tabindex: '-1'
}
});
child.children = [];
node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
assert.lengthOf(results.passes, 1);
});

it('should incomplete when vNode has no children and is type 1', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text',
nodeType: 1
}
});

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 1);
assert.lengthOf(results.passes, 0);
});

it('should fail when tabIndex is NaN', function () {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
role: 'text'
}
});
var child = new axe.SerialVirtualNode({
nodeName: 'a',
attributes: {
tabindex: 'foo',
href: '#'
}
});

node.children = [child];

var results = axe.runVirtualRule('aria-text', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.incomplete, 0);
});
});