Custom Rules

Link to Custom Rules copied to clipboard

The custom rules feature allows you to create new rules to test against.

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

  • Not applicable: the name is null.
  • Fail: the name is not null but is empty.
  • Success: the name is not null and has some length.

Creating the Rule

  1. Set up a new class object that inherits from AxeRule.

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

class CustomRule: AxeRule {

    public init() {
        let ruleConfiguration = RuleConf(impact: AxeImpact.MODERATE,
                                         standard: AxeStandard.BEST_PRACTICE,
                                         summary: "Asserts that when name is present it is not empty.")
        super.init(ruleConf: ruleConfiguration)
    }
}
  1. Next, you'll need to collect the view's properties to test against. For our example, we just need to access the name property.
   override func collectProps(for node: AxeView) -> AxeProps {
        let axeProps = AxeProps()
        axeProps.add([
            .name: node.name
        ])

        return axeProps
   }
  1. Lastly, we'll specify in the runRule function what we're testing against to determine whether the rule has succeeded, failed, or is considered non-applicable.
    override func runRule(_ props: AxeProps) throws -> AxeStatus {
        guard let name = props.get(.name, withType: String.self) else { return .INAPPLICABLE }

        if !name.isEmpty {
            return AxeStatus.PASS
        } else {
            return AxeStatus.FAIL
        }
    }

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. Configurations should be set before any tests run and after authentication.

axe?.configuration.customRules.insert(CustomRule())

Complete Example

class CustomRule: AxeRule {

    public init() {
        let ruleConfiguration = RuleConf(impact: AxeImpact.MODERATE,
                                         standard: AxeStandard.BEST_PRACTICE,
                                         summary: "Asserts that when name is present it is not empty.")
        super.init(ruleConf: ruleConfiguration)
    }

    override func collectProps(for node: AxeView) -> AxeProps {
        let axeProps = AxeProps()
        axeProps.add([
            .name: node.name
        ])

        return axeProps
    }

    override func runRule(_ props: AxeProps) throws -> AxeStatus {
        guard let name = props.get(.name, withType: String.self) else { return .INAPPLICABLE }

        if !name.isEmpty {
            return AxeStatus.PASS
        } else {
            return AxeStatus.FAIL
        }
    }
}