Skip to content

Commit

Permalink
fix(meta-refresh): Add WCAG's 20-hour exception (#3525)
Browse files Browse the repository at this point in the history
* fix(meta-refresh): Add WCAG's 20-hour exception

* put refreshDelay in check data

* Address feedback

* More tweaks
  • Loading branch information
WilcoFiers committed Jul 4, 2022
1 parent 9c6b828 commit 5beb6c3
Show file tree
Hide file tree
Showing 14 changed files with 382 additions and 40 deletions.
8 changes: 8 additions & 0 deletions doc/check-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [css-orientation-lock](#css-orientation-lock)
- [meta-viewport-large](#meta-viewport-large)
- [meta-viewport](#meta-viewport)
- [meta-refresh](#meta-refresh)
- [header-present](#header-present)
- [landmark](#landmark)
- [p-as-heading](#p-as-heading)
Expand Down Expand Up @@ -385,6 +386,13 @@ th</code></pre>
| -------------- | :------ | :------------------------------------------------------------------------------------------- |
| `scaleMinimum` | `2` | The `scale-maximum` CSS value the check applies to. Values above this number will be ignored |

### meta-refresh

| Option | Default | Description |
| ---------- | :------ | :---------------------------------------------------------------------------------- |
| `minDelay` | `0` | Passes if the redirect is equal or less than this. Can be set to `false` to disable |
| `maxDelay` | `7200` | Passes if the redirect is greater than this. Can be set to `false` to disable |

### header-present

<table>
Expand Down
25 changes: 19 additions & 6 deletions lib/checks/navigation/meta-refresh-evaluate.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
function metaRefreshEvaluate(node, options, virtualNode) {
var content = virtualNode.attr('content') || '',
parsedParams = content.split(/[;,]/);
const separatorRegex = /[;,\s]/;
const validRedirectNumRegex = /^[0-9.]+$/;

return content === '' || parsedParams[0] === '0';
}
export default function metaRefreshEvaluate(node, options, virtualNode) {
const { minDelay, maxDelay } = options || {};
const content = (virtualNode.attr('content') || '').trim();
const [redirectStr] = content.split(separatorRegex);
if (!redirectStr.match(validRedirectNumRegex)) {
return true;
}

export default metaRefreshEvaluate;
const redirectDelay = parseFloat(redirectStr);
this.data({ redirectDelay });
if (typeof minDelay === 'number' && redirectDelay <= options.minDelay) {
return true;
}
if (typeof maxDelay === 'number' && redirectDelay > options.maxDelay) {
return true;
}
return false;
}
4 changes: 4 additions & 0 deletions lib/checks/navigation/meta-refresh.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"id": "meta-refresh",
"evaluate": "meta-refresh-evaluate",
"options": {
"minDelay": 0,
"maxDelay": 72000
},
"metadata": {
"impact": "critical",
"messages": {
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/meta-refresh.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "meta-refresh",
"selector": "meta[http-equiv=\"refresh\"]",
"selector": "meta[http-equiv=\"refresh\"][content]",
"excludeHidden": false,
"tags": [
"cat.time-and-media",
Expand Down
181 changes: 150 additions & 31 deletions test/checks/navigation/meta-refresh.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,187 @@ describe('meta-refresh', function() {

var checkContext = axe.testUtils.MockCheckContext();
var checkSetup = axe.testUtils.checkSetup;
var metaRefreshCheck = axe.testUtils.getCheckEvaluate('meta-refresh');

afterEach(function() {
checkContext.reset();
});

describe('; separator', function() {
it('should return false if content value is not 0', function() {
it('returns false if there is a number', function() {
var checkArgs = checkSetup('<meta id="target" name="refresh" content="3">');
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

describe('returns false when valid', function() {
it('there is a decimal', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="300">'
'<meta id="target" name="refresh" content="3.1">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

assert.isFalse(
axe.testUtils
.getCheckEvaluate('meta-refresh')
.apply(checkContext, checkArgs)
it('there is a number followed by a dot', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3.">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('should return false if content value does not start with 0', function() {
it('there is a dot followed by a number', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="300;URL=something.html">'
'<meta id="target" name="refresh" content=".5">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

assert.isFalse(
axe.testUtils
.getCheckEvaluate('meta-refresh')
.apply(checkContext, checkArgs)
it('there is whitespace before the number', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content=" \n\t3">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

describe('with a valid separator', function() {
it('the number is followed by a semicolon', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3;">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('the number is followed by a comma', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3,">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('the number is followed spaces, and then a separator', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3 \t\n;">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('the separator is followed by non-separator characters', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3; https://deque.com/">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('the separator is a space', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3 https://deque.com/">'
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});
});
});

it('should return true if content value starts with 0', function() {
describe('returns true when invalid', function() {
it('the number is prefaced with a plus', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="0;URL=something.html">'
'<meta id="target" name="refresh" content="+3">'
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

assert.isTrue(
axe.testUtils
.getCheckEvaluate('meta-refresh')
.apply(checkContext, checkArgs)
it('the number is prefaced with a minus', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="-3">'
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('should return true if content value is 0', function() {
it('the number is prefaced with a letter', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="0">'
'<meta id="target" name="refresh" content="a3">'
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

assert.isTrue(
axe.testUtils
.getCheckEvaluate('meta-refresh')
.apply(checkContext, checkArgs)
it('the number is followed by an invalid separator character', function() {
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3: https://deque.com/">'
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});
});

it('should return true if there is no content value', function() {
var checkArgs = checkSetup('<meta id="target" name="refresh">');
describe('options.minDelay', function() {
it('returns false when the redirect number is greater than minDelay', function() {
var options = { minDelay: 2 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

assert.isTrue(
axe.testUtils
.getCheckEvaluate('meta-refresh')
.apply(checkContext, checkArgs)
it('returns true when the redirect number equals minDelay', function() {
var options = { minDelay: 3 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('returns true when the redirect number is less than minDelay', function() {
var options = { minDelay: 4 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('ignores minDelay when set to false', function() {
var options = { minDelay: false };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="0">',
options
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});
});

describe('options.maxDelay', function() {
it('returns true when the redirect number is greater than maxDelay', function() {
var options = { maxDelay: 2 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isTrue(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('returns false when the redirect number equals maxDelay', function() {
var options = { maxDelay: 3 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('returns false when the redirect number is less than maxDelay', function() {
var options = { maxDelay: 4 };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="3">',
options
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});

it('ignores maxDelay when set to false', function() {
var options = { maxDelay: false };
var checkArgs = checkSetup(
'<meta id="target" name="refresh" content="9999">',
options
);
assert.isFalse(metaRefreshCheck.apply(checkContext, checkArgs));
});
});
});
18 changes: 18 additions & 0 deletions test/integration/full/meta-refresh/meta-refresh-fail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
describe('meta-refresh fail', function() {
'use strict';

it('should be a violation', function(done) {
axe.run({ runOnly: 'meta-refresh' }, function(err, results) {
try {
assert.isNull(err);
assert.lengthOf(results.violations, 1, 'violations');
assert.lengthOf(results.passes, 0, 'passes');
assert.lengthOf(results.incomplete, 0, 'passes');
assert.lengthOf(results.inapplicable, 0, 'inapplicable');
done();
} catch (e) {
done(e);
}
});
});
});
28 changes: 28 additions & 0 deletions test/integration/full/meta-refresh/meta-refresh-fail1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8" />
<title>Meta-refresh fail 1</title>
<meta http-equiv="refresh" content="10 https://deque.com/">
<link
rel="stylesheet"
type="text/css"
href="/node_modules/mocha/mocha.css"
/>
<script src="/node_modules/mocha/mocha.js"></script>
<script src="/node_modules/chai/chai.js"></script>
<script src="/axe.js"></script>
<script>
mocha.setup({
timeout: 10000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<div id="mocha"></div>
<script src="meta-refresh-fail.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
18 changes: 18 additions & 0 deletions test/integration/full/meta-refresh/meta-refresh-inapplicable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
describe('meta-refresh inapplicable', function() {
'use strict';

it('should be inapplicable', function(done) {
axe.run({ runOnly: 'meta-refresh' }, function(err, results) {
try {
assert.isNull(err);
assert.lengthOf(results.violations, 0, 'violations');
assert.lengthOf(results.passes, 0, 'passes');
assert.lengthOf(results.incomplete, 0, 'passes');
assert.lengthOf(results.inapplicable, 1, 'inapplicable');
done();
} catch (e) {
done(e);
}
});
});
});
29 changes: 29 additions & 0 deletions test/integration/full/meta-refresh/meta-refresh-inapplicable1.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8" />
<title>Meta-refresh inapplicable 1</title>
<!-- no content attribute -->
<meta http-equiv="refresh">
<link
rel="stylesheet"
type="text/css"
href="/node_modules/mocha/mocha.css"
/>
<script src="/node_modules/mocha/mocha.js"></script>
<script src="/node_modules/chai/chai.js"></script>
<script src="/axe.js"></script>
<script>
mocha.setup({
timeout: 10000,
ui: 'bdd'
});
var assert = chai.assert;
</script>
</head>
<body>
<div id="mocha"></div>
<script src="meta-refresh-inapplicable.js"></script>
<script src="/test/integration/adapter.js"></script>
</body>
</html>
Loading

0 comments on commit 5beb6c3

Please sign in to comment.