Custom Rules

Link to Custom Rules copied to clipboard

Supported within:
XML Compose

The custom rules feature lets you create new rules to test against and change key behaviors within existing rules.

note

Custom rules are supported in both XML and Compose Layouts. The implementation of a custom rule itself will vary from XML to Compose, but both will extend the AxeRuleViewHierarchy class. The below example shows the XML implementation.

Let's create a custom rule that checks for the name property of a view. The result criteria will be as follows:

  • Fail: the name is empty.
  • Success: the name has some length.

Creating the Rule

  1. Setup a new class object that inherits from AxeDevToolsRule.

You can set up the rule configuration within the initializer to specify the impact, what standard you're testing against, and add a summary.

class ExampleRule : AxeDevToolsRule(
    AxeStandard.WCAG_21,
    AxeImpact.MINOR.value,
    "Ensure AT and non-AT users have the same experience of understanding labels") {
    
}
  1. You'll need to collect the view's properties to test. For our example, we need to access the name property.
    override fun collectProps(axeDevToolsView: AxeDevToolsView, axeProps: AxeProps) {
        axeProps[AxeProps.Name.VISIBLE_TEXT] = axeDevToolsView.text
    }
  1. Determine if the rule applies to the view by ensuring the visible text is not null.
    override fun isApplicable(axeProps: AxeProps): Boolean {
        val visibleText = axeProps.get(AxeProps.Name.VISIBLE_TEXT, String::class.java)
        return visibleText != null && super.isApplicable(axeProps)
    }
  1. Lastly, we'll specify in the runRule function what we're testing against to determine whether the rule has succeeded or failed.
    override fun runRule(axeProps: AxeProps): String {
        if (super.runRule(axeProps).isNotEmpty()) {
            return super.runRule(axeProps)
        }
        val visibleText = axeProps.get(AxeProps.Name.VISIBLE_TEXT, String::class.java)
        return if (visibleText.isEmpty()) {
            AxeStatus.FAIL
        } else {
            AxeStatus.PASS
        }
    }

Add to axe DevTools

Once you've written the logic for your custom rule, add it to the list of rules axe DevTools knows about by adjusting the configuration when the AxeDevTools object is initialized. Set configurations before any tests run.

val axe = AxeDevTools()

init {
    axe.addCustomRule(CustomRule::class.java)
} 

Complete Example

class ExampleRule : AxeDevToolsRule(
    AxeStandard.WCAG_21,
    AxeImpact.MINOR.value,
    "Ensure AT and non-AT users have the same experience of understanding labels"
) {
    override fun collectProps(axeDevToolsView: AxeDevToolsView, axeProps: AxeProps) {
        axeProps[AxeProps.Name.VISIBLE_TEXT] = axeDevToolsView.text
    }

    override fun isApplicable(axeProps: AxeProps): Boolean {
        val visibleText = axeProps.get(AxeProps.Name.VISIBLE_TEXT, String::class.java)
        return visibleText != null && super.isApplicable(axeProps)
    }

    override fun runRule(axeProps: AxeProps): String {
        if (super.runRule(axeProps).isNotEmpty()) {
            return super.runRule(axeProps)
        }
        val visibleText = axeProps.get(AxeProps.Name.VISIBLE_TEXT, String::class.java)
        return if (visibleText.isEmpty()) {
            AxeStatus.FAIL
        } else {
            AxeStatus.PASS
        }
    }
}