diff --git a/lib/checks/language/has-lang.js b/lib/checks/language/has-lang.js
index cc6da92678..aba70aaf4f 100644
--- a/lib/checks/language/has-lang.js
+++ b/lib/checks/language/has-lang.js
@@ -1,5 +1,10 @@
-return !!(
- node.getAttribute('lang') ||
- node.getAttribute('xml:lang') ||
- ''
-).trim();
+const { isXHTML } = axe.utils;
+
+const langValue = (node.getAttribute(`lang`) || '').trim();
+const xmlLangValue = (node.getAttribute(`xml:lang`) || '').trim();
+
+if (!langValue && !isXHTML(document)) {
+ return false;
+}
+
+return !!(langValue || xmlLangValue);
diff --git a/test/checks/language/has-lang.js b/test/checks/language/has-lang.js
index cfef076052..a4e06cf1db 100644
--- a/test/checks/language/has-lang.js
+++ b/test/checks/language/has-lang.js
@@ -15,9 +15,15 @@ describe('has-lang', function() {
assert.isTrue(checks['has-lang'].evaluate(node));
});
- it('should return true if xml:lang attribute is present', function() {
+ it('should return false if only `xml:lang` attribute is present', function() {
fixture.innerHTML = '
';
+ assert.isFalse(checks['has-lang'].evaluate(fixture.firstChild));
+ });
+
+ it('should return true if both `lang` and `xml:lang` attribute is present', function() {
+ fixture.innerHTML = '';
+
assert.isTrue(checks['has-lang'].evaluate(fixture.firstChild));
});
diff --git a/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.html b/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.html
new file mode 100644
index 0000000000..5207908988
--- /dev/null
+++ b/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.html
@@ -0,0 +1,33 @@
+
+
+
+ html-has-lang test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.js b/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.js
new file mode 100644
index 0000000000..fe3ead2ef3
--- /dev/null
+++ b/test/integration/full/html-has-lang/html-has-lang-fail-xml-lang.js
@@ -0,0 +1,37 @@
+/**
+ * Note:
+ * This rule does not include `iframe` uses matches "window-is-top.js"
+ */
+describe('html-has-lang fail test', function() {
+ 'use strict';
+
+ var results;
+ before(function(done) {
+ axe.testUtils.awaitNestedLoad(function() {
+ axe.run(
+ { runOnly: { type: 'rule', values: ['html-has-lang'] } },
+ function(err, r) {
+ assert.isNull(err);
+ results = r;
+ done();
+ }
+ );
+ });
+ });
+
+ describe('violations', function() {
+ it('should find 1 violations', function() {
+ assert.lengthOf(results.violations, 1);
+ });
+
+ it('should find #fail1', function() {
+ assert.deepEqual(results.violations[0].nodes[0].target, ['#fail1']);
+ });
+ });
+
+ describe('passes', function() {
+ it('should find 0 passes', function() {
+ assert.lengthOf(results.passes, 0);
+ });
+ });
+});
diff --git a/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.js b/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.js
new file mode 100644
index 0000000000..fba87e4778
--- /dev/null
+++ b/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.js
@@ -0,0 +1,36 @@
+/**
+ * Note:
+ * This rule does not include `iframe` uses matches "window-is-top.js"
+ */
+describe('html-has-lang pass test', function() {
+ 'use strict';
+ var results;
+ before(function(done) {
+ axe.testUtils.awaitNestedLoad(function() {
+ axe.run(
+ { runOnly: { type: 'rule', values: ['html-has-lang'] } },
+ function(err, r) {
+ assert.isNull(err);
+ results = r;
+ done();
+ }
+ );
+ });
+ });
+
+ describe('violations', function() {
+ it('should find 0 violations', function() {
+ assert.lengthOf(results.violations, 0);
+ });
+ });
+
+ describe('passes', function() {
+ it('should find 1', function() {
+ assert.lengthOf(results.passes[0].nodes, 1);
+ });
+
+ it('should find #pass1', function() {
+ assert.deepEqual(results.passes[0].nodes[0].target, ['#pass1']);
+ });
+ });
+});
diff --git a/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.xhtml b/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.xhtml
new file mode 100644
index 0000000000..69a9ab273d
--- /dev/null
+++ b/test/integration/full/html-has-lang/html-has-lang-pass-xhtml.xhtml
@@ -0,0 +1,33 @@
+
+
+
+ html-has-lang test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+