diff --git a/lib/checks/language/valid-lang-evaluate.js b/lib/checks/language/valid-lang-evaluate.js index 6a2460b440..b59970281d 100644 --- a/lib/checks/language/valid-lang-evaluate.js +++ b/lib/checks/language/valid-lang-evaluate.js @@ -1,5 +1,6 @@ import { isValidLang, getBaseLang } from '../../core/utils'; import { sanitize } from '../../commons/text'; +import { hasLangText } from '../../commons/dom' function validLangEvaluate(node, options, virtualNode) { const invalid = []; @@ -25,12 +26,17 @@ function validLangEvaluate(node, options, virtualNode) { } }); - if (invalid.length) { - this.data(invalid); - return true; + if (!invalid.length) { + return false; } - - return false; + if ( // Except for `html`, ignore elements with no text + virtualNode.props.nodeName !== 'html' && + !hasLangText(virtualNode) + ) { + return false; + } + this.data(invalid); + return true; } export default validLangEvaluate; diff --git a/lib/commons/dom/has-content-virtual.js b/lib/commons/dom/has-content-virtual.js index 8dfc7e74c7..9d28a05802 100644 --- a/lib/commons/dom/has-content-virtual.js +++ b/lib/commons/dom/has-content-virtual.js @@ -2,25 +2,30 @@ import isVisualContent from './is-visual-content'; import labelVirtual from '../aria/label-virtual'; const hiddenTextElms = [ - 'HEAD', - 'TITLE', - 'TEMPLATE', - 'SCRIPT', - 'STYLE', - 'IFRAME', - 'OBJECT', - 'VIDEO', - 'AUDIO', - 'NOSCRIPT' + 'head', + 'title', + 'template', + 'script', + 'style', + 'iframe', + 'object', + 'video', + 'audio', + 'noscript' ]; -function hasChildTextNodes(elm) { - if (!hiddenTextElms.includes(elm.actualNode.nodeName.toUpperCase())) { - return elm.children.some( - ({ actualNode }) => - actualNode.nodeType === 3 && actualNode.nodeValue.trim() - ); +/** + * Test if the element has child nodes that are non-empty text nodes + * @param {VirtualNode} elm + * @returns boolean + */ +export function hasChildTextNodes(elm) { + if (hiddenTextElms.includes(elm.props.nodeName)) { + return false } + return elm.children.some(({ props }) => { + return props.nodeType === 3 && props.nodeValue.trim() + }) } /** diff --git a/lib/commons/dom/has-lang-text.js b/lib/commons/dom/has-lang-text.js new file mode 100644 index 0000000000..30538f07c2 --- /dev/null +++ b/lib/commons/dom/has-lang-text.js @@ -0,0 +1,24 @@ +import { hasChildTextNodes } from './has-content-virtual'; +import isVisualContent from './is-visual-content'; +import isVisible from './is-visible'; + +/** + * Check that a node has text, or an accessible name which language is defined by the + * nearest ancestor's lang attribute. + * @param {VirtualNode} virtualNode + * @return boolean + */ +export default function hasLangText(virtualNode) { + if (typeof virtualNode.children === 'undefined' || hasChildTextNodes(virtualNode)) { + return true; + } + if (virtualNode.props.nodeType === 1 && isVisualContent(virtualNode)) { + // See: https://github.com/dequelabs/axe-core/issues/3281 + return !!axe.commons.text.accessibleTextVirtual(virtualNode); + } + return virtualNode.children.some(child => ( + !child.attr('lang') && // non-empty lang + hasLangText(child) && // has text + isVisible(child, true) // Not hidden for AT + )); +} diff --git a/lib/commons/dom/index.js b/lib/commons/dom/index.js index 5acd13c076..6e60cec766 100644 --- a/lib/commons/dom/index.js +++ b/lib/commons/dom/index.js @@ -17,6 +17,7 @@ export { default as getTextElementStack } from './get-text-element-stack'; export { default as getViewportSize } from './get-viewport-size'; export { default as hasContentVirtual } from './has-content-virtual'; export { default as hasContent } from './has-content'; +export { default as hasLangText } from './has-lang-text'; export { default as idrefs } from './idrefs'; export { default as insertedIntoFocusOrder } from './inserted-into-focus-order'; export { default as isCurrentPageLink } from './is-current-page-link'; diff --git a/lib/commons/dom/is-visual-content.js b/lib/commons/dom/is-visual-content.js index ef8b7c0aa8..3efef3c870 100644 --- a/lib/commons/dom/is-visual-content.js +++ b/lib/commons/dom/is-visual-content.js @@ -1,8 +1,13 @@ +import { getNodeFromTree } from '../../core/utils'; +import AbstractVirtualNode from '../../core/base/virtual-node/abstract-virtual-node'; + const visualRoles = [ 'checkbox', 'img', + 'meter', + 'progressbar', + 'scrollbar', 'radio', - 'range', 'slider', 'spinbutton', 'textbox' @@ -13,34 +18,34 @@ const visualRoles = [ * @method isVisualContent * @memberof axe.commons.dom * @instance - * @param {Element} element The element to check + * @param {Element|VirtualNode} element The element to check * @return {Boolean} */ -function isVisualContent(element) { - /*eslint indent: 0*/ - const role = element.getAttribute('role'); +function isVisualContent(el) { + const vNode = el instanceof AbstractVirtualNode ? el : getNodeFromTree(el); + const role = axe.commons.aria.getExplicitRole(vNode); if (role) { return visualRoles.indexOf(role) !== -1; } - switch (element.nodeName.toUpperCase()) { - case 'IMG': - case 'IFRAME': - case 'OBJECT': - case 'VIDEO': - case 'AUDIO': - case 'CANVAS': - case 'SVG': - case 'MATH': - case 'BUTTON': - case 'SELECT': - case 'TEXTAREA': - case 'KEYGEN': - case 'PROGRESS': - case 'METER': + switch (vNode.props.nodeName) { + case 'img': + case 'iframe': + case 'object': + case 'video': + case 'audio': + case 'canvas': + case 'svg': + case 'math': + case 'button': + case 'select': + case 'textarea': + case 'keygen': + case 'progress': + case 'meter': return true; - case 'INPUT': - return element.type !== 'hidden'; + case 'input': + return vNode.props.type !== 'hidden'; default: return false; } diff --git a/lib/rules/valid-lang.json b/lib/rules/valid-lang.json index 81df179ef8..382b15f949 100644 --- a/lib/rules/valid-lang.json +++ b/lib/rules/valid-lang.json @@ -1,7 +1,6 @@ { "id": "valid-lang", - "selector": "[lang], [xml\\:lang]", - "matches": "not-html-matches", + "selector": "[lang]:not(html), [xml\\:lang]:not(html)", "tags": ["cat.language", "wcag2aa", "wcag312", "ACT"], "actIds": ["de46e4"], "metadata": { diff --git a/test/checks/language/valid-lang.js b/test/checks/language/valid-lang.js index f4f553e77b..5ca9a56a04 100644 --- a/test/checks/language/valid-lang.js +++ b/test/checks/language/valid-lang.js @@ -12,7 +12,7 @@ describe('valid-lang', function() { }); it('should return false if a lang attribute is present in options', function() { - var params = checkSetup('
', { + var params = checkSetup('
text
', { value: ['blah', 'blah', 'woohoo'] }); @@ -20,7 +20,7 @@ describe('valid-lang', function() { }); it('should lowercase options and attribute first', function() { - var params = checkSetup('
', { + var params = checkSetup('
text
', { value: ['blah', 'blah', 'wOohoo'] }); @@ -28,39 +28,39 @@ describe('valid-lang', function() { }); it('should return true if a lang attribute is not present in options', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isTrue(validLangEvaluate.apply(checkContext, params)); assert.deepEqual(checkContext._data, ['lang="FOO"']); }); it('should return false (and not throw) when given no present in options', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isFalse(validLangEvaluate.apply(checkContext, params)); }); it('should return true if the language is badly formatted', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isTrue(validLangEvaluate.apply(checkContext, params)); assert.deepEqual(checkContext._data, ['lang="en_US"']); }); it('should return false if it matches a substring proceeded by -', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isFalse(validLangEvaluate.apply(checkContext, params)); }); it('should work with xml:lang', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isFalse(validLangEvaluate.apply(checkContext, params)); }); it('should accept options.attributes', function() { - var params = checkSetup('
', { + var params = checkSetup('
text
', { attributes: ['custom-lang'] }); @@ -69,8 +69,15 @@ describe('valid-lang', function() { }); it('should return true if lang value is just whitespace', function() { - var params = checkSetup('
'); + var params = checkSetup('
text
'); assert.isTrue(validLangEvaluate.apply(checkContext, params)); }); + + it('should return false if a lang attribute element has no content', function() { + var params = checkSetup('
'); + + assert.isFalse(validLangEvaluate.apply(checkContext, params)); + assert.deepEqual(checkContext._data, null); + }); }); diff --git a/test/commons/color/get-background-color.js b/test/commons/color/get-background-color.js index b4fa859961..9fc20d7a3e 100644 --- a/test/commons/color/get-background-color.js +++ b/test/commons/color/get-background-color.js @@ -4,6 +4,7 @@ describe('color.getBackgroundColor', function() { var fixture = document.getElementById('fixture'); var shadowSupported = axe.testUtils.shadowSupport.v1; + var isIE11 = axe.testUtils.isIE11; var origBodyBg; var origHtmlBg; @@ -480,11 +481,11 @@ describe('color.getBackgroundColor', function() { assert.equal(actual.alpha, expected.alpha); }); - it('handles nested inline elements in the middle of a text', function () { + it('handles nested inline elements in the middle of a text', function() { fixture.innerHTML = '
' + '
' + - ' Text '+ + ' Text ' + ' ' + ' ' + '
'; @@ -499,7 +500,7 @@ describe('color.getBackgroundColor', function() { assert.equal(actual.alpha, 1); }); - it('should return null for inline elements with position:absolute', function () { + it('should return null for inline elements with position:absolute', function() { fixture.innerHTML = '
' + '
' + @@ -725,21 +726,25 @@ describe('color.getBackgroundColor', function() { assert.isNull(actual); }); - it('should return background color for inline elements that do not fit the viewport', function() { - var html = ''; - for (var i = 0; i < 300; i++) { - html += 'foo
'; - } - fixture.innerHTML = '' + html + ''; - axe.testUtils.flatTreeSetup(fixture); - var actual = axe.commons.color.getBackgroundColor(fixture, []); - var expected = new axe.commons.color.Color(255, 255, 255, 1); + // Test is flakey in IE11, timing out regularly + (isIE11 ? xit : it)( + 'should return background color for inline elements that do not fit the viewport', + function() { + var html = ''; + for (var i = 0; i < 300; i++) { + html += 'foo
'; + } + fixture.innerHTML = '' + html + ''; + axe.testUtils.flatTreeSetup(fixture); + var actual = axe.commons.color.getBackgroundColor(fixture, []); + var expected = new axe.commons.color.Color(255, 255, 255, 1); - assert.closeTo(actual.red, expected.red, 0.5); - assert.closeTo(actual.green, expected.green, 0.5); - assert.closeTo(actual.blue, expected.blue, 0.5); - assert.closeTo(actual.alpha, expected.alpha, 0.1); - }); + assert.closeTo(actual.red, expected.red, 0.5); + assert.closeTo(actual.green, expected.green, 0.5); + assert.closeTo(actual.blue, expected.blue, 0.5); + assert.closeTo(actual.alpha, expected.alpha, 0.1); + } + ); it('should return the body bgColor when content does not overlap', function() { fixture.innerHTML = diff --git a/test/commons/dom/has-lang-text.js b/test/commons/dom/has-lang-text.js new file mode 100644 index 0000000000..2ed94fdfd1 --- /dev/null +++ b/test/commons/dom/has-lang-text.js @@ -0,0 +1,83 @@ +describe('dom.hasLangText', function() { + 'use strict'; + var hasLangText = axe.commons.dom.hasLangText; + var fixture = document.getElementById('fixture'); + var tree; + + it('returns true when the element has a non-empty text node as its content', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('returns true when the element has nested text node as its content', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('returns false when the element has nested text is hidden', function () { + fixture.innerHTML = '
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isFalse(hasLangText(target)); + }); + + it('returns false when the element has an empty text node as its content', function () { + fixture.innerHTML = '
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isFalse(hasLangText(target)); + }); + + it('returns false if all text is in a child with a lang attribute', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isFalse(hasLangText(target)); + }); + + it('does not skip if lang is on the starting node', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('ignores empty lang attributes', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('ignores null lang attributes', function () { + fixture.innerHTML = '
text
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('true for non-text content with an accessible name', function () { + fixture.innerHTML = '
foo
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isTrue(hasLangText(target)); + }); + + it('false for non-text content without accessible name', function () { + fixture.innerHTML = '
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isFalse(hasLangText(target)); + }); + + it('returns false for non-text content with a lang attr', function () { + fixture.innerHTML = '
foo
'; + tree = axe.utils.getFlattenedTree(fixture); + var target = axe.utils.querySelectorAll(tree, '#target')[0]; + assert.isFalse(hasLangText(target)); + }); +}); diff --git a/test/commons/dom/is-visual-content.js b/test/commons/dom/is-visual-content.js index 1c6d1014f2..701429712b 100644 --- a/test/commons/dom/is-visual-content.js +++ b/test/commons/dom/is-visual-content.js @@ -2,110 +2,119 @@ describe('dom.isVisualContent', function() { 'use strict'; var fixture = document.getElementById('fixture'); + var queryFixture = axe.testUtils.queryFixture; + var isVisualContent = axe.commons.dom.isVisualContent afterEach(function() { - document.getElementById('fixture').innerHTML = ''; + fixture.innerHTML = ''; }); describe('isVisualContent', function() { it('should return true for img', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for iframe', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for object', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for video', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for audio', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for canvas', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for svg', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for math', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for button', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for select', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for textarea', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for keygen', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for meter', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for non-hidden input', function() { - fixture.innerHTML = ''; - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isTrue(isVisualContent(virtualNode)); }); it('should return true for elements with a visual aria role', function() { - fixture.innerHTML = + var virtualNode = queryFixture('
' + '' + '' + '' + - '' + + '' + + '' + + '' + '' + '' + - ''; - - for (var i = 0; i < fixture.children.length; i++) { - assert.isTrue(axe.commons.dom.isVisualContent(fixture.children[i])); + '' + + '
' + ); + + for (var i = 0; i < virtualNode.children.length; i++) { + assert.isTrue( + isVisualContent(virtualNode.children[i]), + 'for role ' + virtualNode.children[i].attr('role') + ); } }); it('should return false for hidden input', function() { - fixture.innerHTML = ''; - assert.isFalse(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture(''); + assert.isFalse(isVisualContent(virtualNode)); }); it('should return false for p', function() { - fixture.innerHTML = '

Paragraph!

'; - assert.isFalse(axe.commons.dom.isVisualContent(fixture.children[0])); + var virtualNode = queryFixture('

Paragraph!

'); + assert.isFalse(isVisualContent(virtualNode)); }); }); }); diff --git a/test/integration/full/isolated-env/isolated-env.html b/test/integration/full/isolated-env/isolated-env.html index defb21a83c..b229f66bfd 100644 --- a/test/integration/full/isolated-env/isolated-env.html +++ b/test/integration/full/isolated-env/isolated-env.html @@ -79,7 +79,7 @@

Ok

I am a circle -
+
English
  • Hello
  • diff --git a/test/integration/rules/valid-lang/valid-lang.html b/test/integration/rules/valid-lang/valid-lang.html index 0dd98d03bc..77f59ec10f 100644 --- a/test/integration/rules/valid-lang/valid-lang.html +++ b/test/integration/rules/valid-lang/valid-lang.html @@ -9,3 +9,12 @@

    Not English

    Mix

    English

    + +

    +

    +

    + English +

    +

    + +

    diff --git a/test/integration/rules/valid-lang/valid-lang.json b/test/integration/rules/valid-lang/valid-lang.json index b8034e1631..6fa8341ffd 100644 --- a/test/integration/rules/valid-lang/valid-lang.json +++ b/test/integration/rules/valid-lang/valid-lang.json @@ -8,6 +8,11 @@ ["#pass3"], ["#pass4"], ["#pass5"], - ["#pass6"] + ["#pass6"], + ["#pass7"], + ["#pass8"], + ["#pass9"], + ["#pass10"], + ["#pass11"] ] } diff --git a/test/integration/virtual-rules/html-lang-valid.js b/test/integration/virtual-rules/html-lang-valid.js new file mode 100644 index 0000000000..d89d410ca6 --- /dev/null +++ b/test/integration/virtual-rules/html-lang-valid.js @@ -0,0 +1,95 @@ +describe('html-lang-valid virtual-rule', function() { + it('is inapplicable without lang or xml:lang', function() { + // Error caught by html-has-lang instead + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: {} + }); + + assert.lengthOf(results.inapplicable, 1); + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass with a valid lang', function() { + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: { + lang: 'en' + } + }); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass with a valid xml:lang', function() { + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: { + 'xml:lang': 'en' + } + }); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should pass for both lang and xml:lang', function() { + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: { + lang: 'en', + 'xml:lang': 'en' + } + }); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + + it('should fail with an invalid lang', function() { + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: { + lang: 'invalid' + } + }); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); + + it('should fail with an invalid xml:lang', function() { + var results = axe.runVirtualRule('html-lang-valid', { + nodeName: 'html', + attributes: { + 'xml:lang': 'invalid' + } + }); + + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); + + it('should fail with an invalid lang, and explicitly no children', function() { + var html = new axe.SerialVirtualNode({ + nodeName: 'html', + attributes: { + lang: 'invalid' + } + }); + html.children = []; + + var results = axe.runVirtualRule('html-lang-valid', html); + assert.lengthOf(results.passes, 0); + assert.lengthOf(results.violations, 1); + assert.lengthOf(results.incomplete, 0); + }); +}); diff --git a/test/rule-matches/not-html-matches.js b/test/rule-matches/not-html-matches.js deleted file mode 100644 index c850c94a9d..0000000000 --- a/test/rule-matches/not-html-matches.js +++ /dev/null @@ -1,20 +0,0 @@ -describe('not-html-matches', function() { - 'use strict'; - - var fixture = document.getElementById('fixture'); - var rule; - - beforeEach(function() { - rule = axe.utils.getRule('valid-lang'); - }); - - it('returns true when element is not the html element', function() { - var vNode = axe.setup(fixture); - assert.isTrue(rule.matches(null, vNode)); - }); - - it('returns false when element is the html element', function() { - var vNode = axe.setup(document.documentElement); - assert.isFalse(rule.matches(null, vNode)); - }); -});