Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

Commit b6a1e06

Browse files
committed
added new rule AvoidAbstractRoleRule
1 parent 3acd1dc commit b6a1e06

File tree

4 files changed

+149
-0
lines changed

4 files changed

+149
-0
lines changed

bitvunit-core/src/main/java/de/codescape/bitvunit/model/Page.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,15 @@ public HtmlTitle findTitleTag() {
293293
return (HtmlTitle) htmlPage.getFirstByXPath("/html/head/title[1]");
294294
}
295295

296+
/**
297+
* Returns all elements with the given attribute name as {@link HtmlElement} instances.
298+
*
299+
* @param attributeName name of the attribute to filter by
300+
* @return all elements with the given attribute name as {@link HtmlElement} instances
301+
*/
302+
@SuppressWarnings("unchecked")
303+
public List<HtmlElement> findAllElementsWithAttribute(String attributeName) {
304+
return (List<HtmlElement>) htmlPage.getByXPath("//*[@" + attributeName + "]");
305+
}
306+
296307
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package de.codescape.bitvunit.rule.page;
2+
3+
import com.gargoylesoftware.htmlunit.html.HtmlElement;
4+
import de.codescape.bitvunit.model.Page;
5+
import de.codescape.bitvunit.rule.AbstractRule;
6+
import de.codescape.bitvunit.rule.Violations;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
/**
12+
* AvoidAbstractRoleRule ensures that no abstract roles that are part of the Accessible Rich Internet Applications
13+
* (WAI-ARIA) 1.0 document are used within the current HTML page.
14+
*
15+
* @author Stefan Glase
16+
* @see <a href="http://www.w3.org/TR/wai-aria/roles#abstract_roles">http://www.w3.org/TR/wai-aria/roles#abstract_roles</a>
17+
* @since 0.6
18+
*/
19+
public class AvoidAbstractRoleRule extends AbstractRule {
20+
21+
private static final String RULE_NAME = "AvoidAbstractRole";
22+
private static final String RULE_MESSAGE = "Abstract roles must not be used inside HTML documents because their only purpose is the aggregation of their child roles.";
23+
private static final List<String> ABSTRACT_ROLES = Arrays.asList("command", "composite", "input", "landmark",
24+
"range", "roletype", "section", "sectionhead", "select", "structure", "widget", "window");
25+
26+
@Override
27+
public String getName() {
28+
return RULE_NAME;
29+
}
30+
31+
@Override
32+
protected void applyTo(Page page, Violations violations) {
33+
List<HtmlElement> elements = page.findAllElementsWithAttribute("role");
34+
for (HtmlElement element : elements) {
35+
String roleValue = element.getAttribute("role");
36+
if (roleValue != null && !roleValue.isEmpty() && ABSTRACT_ROLES.contains(roleValue)) {
37+
violations.add(createViolation(element, RULE_MESSAGE));
38+
}
39+
}
40+
}
41+
42+
}

bitvunit-core/src/main/resources/rulesets/all-rules.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<rule class="de.codescape.bitvunit.rule.media.AlternativeTextForObjectRule"/>
4242

4343
<!-- Page -->
44+
<rule class="de.codescape.bitvunit.rule.page.AvoidAbstractRoleRule"/>
4445
<rule class="de.codescape.bitvunit.rule.page.TitleForPageRule"/>
4546
<rule class="de.codescape.bitvunit.rule.page.LanguageForHtmlTagRule"/>
4647

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package de.codescape.bitvunit.rule.page;
2+
3+
import de.codescape.bitvunit.rule.Violations;
4+
import org.junit.Test;
5+
6+
import static de.codescape.bitvunit.test.Assertions.assertNoViolations;
7+
import static de.codescape.bitvunit.test.Assertions.assertViolations;
8+
import static de.codescape.bitvunit.util.html.HtmlPageUtil.toHtmlPage;
9+
import static org.junit.Assert.assertEquals;
10+
import static org.junit.Assert.assertNotNull;
11+
12+
public class AvoidAbstractRoleRuleTest {
13+
14+
private AvoidAbstractRoleRule rule = new AvoidAbstractRoleRule();
15+
16+
@Test
17+
public void ruleHasAName() throws Exception {
18+
assertNotNull(rule.getName());
19+
assertEquals("AvoidAbstractRole", rule.getName());
20+
}
21+
22+
@Test
23+
public void abstractRoleCommandPresentCausesViolation() throws Exception {
24+
abstractRoleWithRoleNamePresentCausesViolation("command");
25+
}
26+
27+
@Test
28+
public void abstractRoleCompositePresentCausesViolation() throws Exception {
29+
abstractRoleWithRoleNamePresentCausesViolation("composite");
30+
}
31+
32+
@Test
33+
public void abstractRoleInputPresentCausesViolation() throws Exception {
34+
abstractRoleWithRoleNamePresentCausesViolation("input");
35+
}
36+
37+
@Test
38+
public void abstractRoleLandmarkPresentCausesViolation() throws Exception {
39+
abstractRoleWithRoleNamePresentCausesViolation("landmark");
40+
}
41+
42+
@Test
43+
public void abstractRoleRangePresentCausesViolation() throws Exception {
44+
abstractRoleWithRoleNamePresentCausesViolation("range");
45+
}
46+
47+
@Test
48+
public void abstractRoleRoletypePresentCausesViolation() throws Exception {
49+
abstractRoleWithRoleNamePresentCausesViolation("roletype");
50+
}
51+
52+
@Test
53+
public void abstractRoleSectionPresentCausesViolation() throws Exception {
54+
abstractRoleWithRoleNamePresentCausesViolation("section");
55+
}
56+
57+
@Test
58+
public void abstractRoleSectionheadPresentCausesViolation() throws Exception {
59+
abstractRoleWithRoleNamePresentCausesViolation("sectionhead");
60+
}
61+
62+
@Test
63+
public void abstractRoleSelectPresentCausesViolation() throws Exception {
64+
abstractRoleWithRoleNamePresentCausesViolation("select");
65+
}
66+
67+
@Test
68+
public void abstractRoleStructurePresentCausesViolation() throws Exception {
69+
abstractRoleWithRoleNamePresentCausesViolation("structure");
70+
}
71+
72+
@Test
73+
public void abstractRoleWidgetPresentCausesViolation() throws Exception {
74+
abstractRoleWithRoleNamePresentCausesViolation("widget");
75+
}
76+
77+
@Test
78+
public void abstractRoleWindowPresentCausesViolation() throws Exception {
79+
abstractRoleWithRoleNamePresentCausesViolation("window");
80+
}
81+
82+
private void abstractRoleWithRoleNamePresentCausesViolation(String roleName) {
83+
String content = "<html><body><div role=\"" + roleName + "\">Content</div></body></html>";
84+
Violations violations = rule.applyTo(toHtmlPage(content));
85+
assertViolations(violations, rule, 1);
86+
}
87+
88+
@Test
89+
public void landmarkRoleBannerPresentCausesNoViolation() throws Exception {
90+
String content = "<html><body><div role=\"banner\">Content</div></body></html>";
91+
Violations violations = rule.applyTo(toHtmlPage(content));
92+
assertNoViolations(violations, rule);
93+
}
94+
95+
}

0 commit comments

Comments
 (0)