Skip to content

Commit

Permalink
fix(p-as-heading): p-as-heading rule to account for textContent l…
Browse files Browse the repository at this point in the history
…ength (#3145)
  • Loading branch information
Zidious committed Oct 4, 2021
1 parent 5908f0d commit 400a230
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 22 deletions.
19 changes: 18 additions & 1 deletion doc/check-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,25 @@ h6:not([role]),
{ "size": 1.4 }
]</code></pre>
</td>
<td align="left">Common CSS values used to display `p` elements as `h1-h6` elements determining if a `p` element is being improperly repurposed</td>
</tr>
<tr>
<td>
<code>passLength</code>
</td>
<td align="left">
<pre lang=js><code>"passLength": 1</code></pre>
</td>
<td align="left">Relative length, if the the candidate heading is X times or greater the length of the candidate paragraph, it will pass.</td>
</tr>
<tr>
<td>
<code>faiLength</code>
</td>
<td align="left">
<pre lang=js><code>"failLength": 0.5</code></pre>
</td>
<td align="left">Relative length, if the the candidate heading is X times or less the length of the candidate paragraph, it can fail.</td>
</tr>
</tbody>
</table>

Expand Down
2 changes: 1 addition & 1 deletion doc/rule-descriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Rules we are still testing and developing. They are not enabled by default in ax
| [label-content-name-mismatch](https://dequeuniversity.com/rules/axe/4.3/label-content-name-mismatch?application=RuleDescription) | Ensures that elements labelled through their content must have their visible text as part of their accessible name | Serious | cat.semantics, wcag21a, wcag253, experimental | failure | [2ee8b8](https://act-rules.github.io/rules/2ee8b8) |
| [link-in-text-block](https://dequeuniversity.com/rules/axe/4.3/link-in-text-block?application=RuleDescription) | Links can be distinguished without relying on color | Serious | cat.color, experimental, wcag2a, wcag141 | failure, needs&nbsp;review | |
| [no-autoplay-audio](https://dequeuniversity.com/rules/axe/4.3/no-autoplay-audio?application=RuleDescription) | Ensures &lt;video&gt; or &lt;audio&gt; elements do not autoplay audio for more than 3 seconds without a control mechanism to stop or mute the audio | Moderate | cat.time-and-media, wcag2a, wcag142, experimental | failure, needs&nbsp;review | [80f0bf](https://act-rules.github.io/rules/80f0bf) |
| [p-as-heading](https://dequeuniversity.com/rules/axe/4.3/p-as-heading?application=RuleDescription) | Ensure p elements are not used to style headings | Serious | cat.semantics, wcag2a, wcag131, experimental | failure | |
| [p-as-heading](https://dequeuniversity.com/rules/axe/4.3/p-as-heading?application=RuleDescription) | Ensure p elements are not used to style headings | Serious | cat.semantics, wcag2a, wcag131, experimental | failure, needs&nbsp;review | |
| [table-fake-caption](https://dequeuniversity.com/rules/axe/4.3/table-fake-caption?application=RuleDescription) | Ensure that tables with a caption use the &lt;caption&gt; element. | Serious | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | failure | |
| [td-has-header](https://dequeuniversity.com/rules/axe/4.3/td-has-header?application=RuleDescription) | Ensure that each non-empty data cell in a large table has one or more table headers | Critical | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | failure | |

Expand Down
13 changes: 13 additions & 0 deletions lib/checks/navigation/p-as-heading-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ function pAsHeadingEvaluate(node, options, virtualNode) {
const nextStyle = nextSibling ? getStyleValues(nextSibling) : null;
const prevStyle = prevSibling ? getStyleValues(prevSibling) : null;

const optionsPassLength = options.passLength;
const optionsFailLength = options.failLength;

const headingLength = node.textContent.trim().length;
const paragraphLength = nextSibling?.textContent.trim().length;

if (headingLength > paragraphLength * optionsPassLength) {
return true;
}

if (!nextStyle || !isHeaderStyle(currStyle, nextStyle, margins)) {
return true;
}
Expand All @@ -92,6 +102,9 @@ function pAsHeadingEvaluate(node, options, virtualNode) {
return undefined;
}

if (headingLength > paragraphLength * optionsFailLength) {
return undefined;
}
return false;
}

Expand Down
7 changes: 5 additions & 2 deletions lib/checks/navigation/p-as-heading.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
{
"size": 1.4
}
]
],
"passLength": 1,
"failLength": 0.5
},
"metadata": {
"impact": "serious",
"messages": {
"pass": "<p> elements are not styled as headings",
"fail": "Heading elements should be used instead of styled p elements"
"fail": "Heading elements should be used instead of styled <p> elements",
"incomplete": "Unable to determine if <p> elements are styled as headings"
}
}
}
83 changes: 70 additions & 13 deletions test/checks/navigation/p-as-heading.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('p-as-heading', function() {

it('returns false if the font-weight is heavier', function() {
var params = checkSetup(
'<p id="target" style="font-weight:bold">elm 1</p>' + '<p>elm 2</p>',
'<p id="target" style="font-weight:bold">elm 1</p>' + '<p>elm 2elm 2</p>',
testOptions
);
assert.isFalse(
Expand All @@ -46,7 +46,7 @@ describe('p-as-heading', function() {

it('returns false if the font-size is bigger', function() {
var params = checkSetup(
'<p id="target" style="font-size:150%">elm 1</p> <p>elm 2</p>',
'<p id="target" style="font-size:150%">elm 1</p> <p>elm 2elm 2</p>',
testOptions
);
assert.isFalse(
Expand All @@ -56,7 +56,7 @@ describe('p-as-heading', function() {

it('returns false if the fake heading is italic and the text is not', function() {
var params = checkSetup(
'<p id="target" style="font-style:italic">elm 1</p> <p>elm 2</p>',
'<p id="target" style="font-style:italic">elm 1</p> <p>elm 2elm 2</p>',
testOptions
);
assert.isFalse(
Expand All @@ -67,7 +67,7 @@ describe('p-as-heading', function() {
it('returns true if both texts are bold, italic and larger', function() {
var params = checkSetup(
'<p id="target" style="font-weight:bold; font-size:120%; font-style:italic">elm 1</p>' +
'<p style="font: italic bold 120% bold">elm 2</p>',
'<p style="font: italic bold 120% bold">elm 2elm 2</p>',
testOptions
);
assert.isTrue(
Expand All @@ -77,7 +77,7 @@ describe('p-as-heading', function() {

it('considers styles of elements inside the paragraph', function() {
var params = checkSetup(
'<p id="target"><b>elm 1</b></p> <p>elm 2</p>',
'<p id="target"><b>elm 1</b></p> <p>elm 2elm 2</p>',
testOptions
);
assert.isFalse(
Expand All @@ -87,7 +87,7 @@ describe('p-as-heading', function() {

it('ignores empty child element for style', function() {
var params = checkSetup(
'<p id="target"><span> </span><b>elm 1</b></p> <p>elm 2</p>',
'<p id="target"><span> </span><b>elm 1</b></p> <p>elm 2elm 2</p>',
testOptions
);
assert.isFalse(
Expand All @@ -97,7 +97,7 @@ describe('p-as-heading', function() {

it('considers styles of elements that do not contain all the text', function() {
var params = checkSetup(
'<p id="target"><b>elm</b> 1</p> <p>elm 2</p>',
'<p id="target"><b>elm</b> 1</p> <p>elm 2elm 2</p>',
testOptions
);
assert.isTrue(
Expand All @@ -108,7 +108,7 @@ describe('p-as-heading', function() {
it('returns undefined instead of false if the element is inside a blockquote', function() {
var params = checkSetup(
'<blockquote>' +
'<p style="font-weight:bold" id="target">elm 1</p> <p>elm 2</p>' +
'<p style="font-weight:bold" id="target">elm 1</p> <p>elm 2elm 2</p>' +
'</blockquote>',
testOptions
);
Expand All @@ -120,7 +120,7 @@ describe('p-as-heading', function() {
it('returns true over undefined from within a blockquote', function() {
var params = checkSetup(
'<blockquote>' +
'<p id="target">elm 1</p> <p>elm 2</p>' +
'<p id="target">elm 1</p> <p>elm 2elm 2</p>' +
'</blockquote>',
testOptions
);
Expand All @@ -141,12 +141,68 @@ describe('p-as-heading', function() {
);
});

it('returns true if the heading is greater than the paragraph', function() {
var params = checkSetup(
'<p id="target">elm1elm1</p>' + '<p>elm2</p>',
testOptions
);
assert.isTrue(
axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)
);
});

it('returns undefined if the heading is twice as long but not greater than the length of the pararaph', function() {
var params = checkSetup(
'<p id="target" style="font-weight:bold">elm1elm</p>' + '<p>elm2elm2</p>',
testOptions
);
assert.isUndefined(
axe.testUtils.getCheckEvaluate('p-as-heading').apply(checkContext, params)
);
});

describe('options.passLength and options.failLength', function() {
it('returns true if the heading is greater than the paragraph using options.passLength', function() {
var options = {
margins: [{ weight: 100 }, { italic: true }, { size: 1.2 }],
passLength: 2
};

var params = checkSetup(
'<p id="target">elm1elm1elm1</p>' + '<p>elm2</p>',
options
);
assert.isTrue(
axe.testUtils
.getCheckEvaluate('p-as-heading')
.apply(checkContext, params)
);
});

it('returns undefined if the heading is twice as long but not greater than the length of the pararaph using options.failLength ', function() {
var options = {
margins: [{ weight: 100 }, { italic: true }, { size: 1.2 }],
failLength: 0.6
};
var params = checkSetup(
'<p id="target" style="font-weight:bold">elm1elm</p>' +
'<p>elm2elm2elm2</p>',
options
);
assert.isFalse(
axe.testUtils
.getCheckEvaluate('p-as-heading')
.apply(checkContext, params)
);
});
});

describe('option.margin', function() {
it('passes if no margins are set', function() {
var options = {};

var params = checkSetup(
'<p id="target"><b>elm 1</b></p> <p>elm 2</p>',
'<p id="target"><b>elm 1</b></p> <p>elm 2elm 2</p>',
options
);
assert.isTrue(
Expand All @@ -162,7 +218,7 @@ describe('p-as-heading', function() {
};

var params = checkSetup(
'<p id="target"><b>elm 1</b></p> <p>elm 2</p>',
'<p id="target"><b>elm 1</b></p> <p>elm 2elm 2</p>',
options
);
assert.isTrue(
Expand All @@ -179,7 +235,7 @@ describe('p-as-heading', function() {

var params = checkSetup(
'<p id="target" style="font-size:1.5em; font-weight:bold">elm 1</p>' +
'<p>elm 2</p>',
'<p>elm 2elm 2</p>',
options
);
assert.isFalse(
Expand Down Expand Up @@ -211,7 +267,8 @@ describe('p-as-heading', function() {
};

var params = checkSetup(
'<p id="target" style="font-style:italic">elm 1</p>' + '<p>elm 2</p>',
'<p id="target" style="font-style:italic">elm 1</p>' +
'<p>elm 2elm 2</p>',
options
);
assert.isFalse(
Expand Down
13 changes: 9 additions & 4 deletions test/integration/rules/p-as-heading/p-as-heading.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,34 @@
<p><b>A paragraph!</b></p>
</div>

<div>
<p id="passed6">Hello World</p>
<p>Hello</p>
</div>

<!-- Failures -->
<div>
<p id="failed1" style="font-weight:bold; font-size: 120%">Some text</p>
<p>A paragraph!</p>
<p>A paragraph, A paragraph!</p>
</div>

<div>
<p id="failed2" style="font-size:150%">Some text</p>
<p>A paragraph!</p>
<p>A paragraph, A paragraph!</p>
</div>

<div>
<p id="failed3">
<b><i>Some text</i></b>
</p>
<p>A paragraph!</p>
<p>A paragraph, A paragraph!</p>
</div>

<!-- Cant Tell -->
<div>
<p id="passed5"><b style="font-size:120%">Some text</b></p>
<p id="canttell1"><b style="font-size:120%">Some text</b></p>
<p>A paragraph!</p>
<p>A paragraph, A paragraph!</p>
</div>

<div>
Expand Down
3 changes: 2 additions & 1 deletion test/integration/rules/p-as-heading/p-as-heading.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
["#passed2"],
["#passed3"],
["#passed4"],
["#passed5"]
["#passed5"],
["#passed6"]
],
"incomplete": [["#canttell1"], ["#canttell2"]]
}

0 comments on commit 400a230

Please sign in to comment.