Skip to content

Commit

Permalink
Fix Issue #161 - [Feature Request] Add rule to ensure the descendants…
Browse files Browse the repository at this point in the history
… of off screen elements are also off screen. (#326)

The evaluation strategy is to  test each element against its parent. This avoids starting with a root element and testing all its descendants. The latter would be very inefficient because the test would be re-run for each descendant of the root.

Also, the chosen strategy has the added benefit of failing while on the element with the mismatched IsOffscreen property value, which means it is easier to point users to the element with the problem. This would have been much more difficult if the non matching element were a deep descendant of the element being tested.
  • Loading branch information
RobGallo authored Mar 13, 2020
1 parent 35d23d2 commit 9524eab
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Core/Enums/RuleId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,5 +258,6 @@ public enum RuleId

ClickablePointOnScreen,
ClickablePointOffScreen,
IsOffScreenChild,
}
}
36 changes: 36 additions & 0 deletions src/Rules/Library/IsOffScreenChild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Axe.Windows.Core.Bases;
using Axe.Windows.Core.Enums;
using Axe.Windows.Core.Types;
using Axe.Windows.Rules.Resources;
using System;
using static Axe.Windows.Rules.PropertyConditions.BoolProperties;
using static Axe.Windows.Rules.PropertyConditions.Relationships;

namespace Axe.Windows.Rules.Library
{
[RuleInfo(ID = RuleId.IsOffScreenChild)]
class IsOffScreenChild : Rule
{
public IsOffScreenChild()
{
this.Info.Description = Descriptions.IsOffScreenChild;
this.Info.HowToFix = HowToFix.IsOffScreenChild;
this.Info.Standard = A11yCriteriaId.ObjectInformation;
this.Info.PropertyID = PropertyType.UIA_IsOffscreenPropertyId;
}

public override EvaluationCode Evaluate(IA11yElement e)
{
if (e == null) throw new ArgumentNullException(nameof(e));

return Parent(IsOffScreen).Matches(e) ? EvaluationCode.Error : EvaluationCode.Pass;
}

protected override Condition CreateCondition()
{
return ~IsOffScreen;
}
} // class
} // namespace
9 changes: 9 additions & 0 deletions src/Rules/Resources/Descriptions.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Rules/Resources/Descriptions.resx
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,7 @@
<data name="ClickablePointOffScreen" xml:space="preserve">
<value>An element's IsOffScreen property must be true when its clickable point is off screen.</value>
</data>
<data name="IsOffScreenChild" xml:space="preserve">
<value>Children of off-screen element's must also be off-screen.</value>
</data>
</root>
10 changes: 10 additions & 0 deletions src/Rules/Resources/HowToFix.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/Rules/Resources/HowToFix.resx
Original file line number Diff line number Diff line change
Expand Up @@ -554,4 +554,8 @@ If the element's ClickablePoint property is incorrect, please ensure it returns
<value>If the element's ClickablePoint property is correct, set the element's IsOffScreen property to true.
If the element's ClickablePoint property is incorrect, please ensure it returns the correct value.</value>
</data>
<data name="IsOffScreenChild" xml:space="preserve">
<value>If the element is meant to be visible, set the element's parent's IsOffscreen property to false.
If the element is not meant to be visible, set the element's IsOffscreen property to true.</value>
</data>
</root>
65 changes: 65 additions & 0 deletions src/RulesTest/Library/IsOffScreenChild.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EvaluationCode = Axe.Windows.Rules.EvaluationCode;

namespace Axe.Windows.RulesTest.Library
{
[TestClass]
public class IsOffScreenChildTests
{
private static Axe.Windows.Rules.IRule Rule = new Axe.Windows.Rules.Library.IsOffScreenChild();

[TestMethod]
public void IsOffScreenChild_Condition_Matches()
{
using (var e = new MockA11yElement())
{
e.IsOffScreen = false;
Assert.IsTrue(Rule.Condition.Matches(e));
} // using
}

[TestMethod]
public void IsOffScreenChild_Condition_NoMatch()
{
using (var e = new MockA11yElement())
{
e.IsOffScreen = true;
Assert.IsFalse(Rule.Condition.Matches(e));
} // using
}

[TestMethod]
public void IsOffScreenChild_Evaluate_Error()
{
using (var e = new MockA11yElement())
using (var parent = new MockA11yElement())
{
parent.IsOffScreen = true;
e.Parent = parent;
Assert.AreEqual(EvaluationCode.Error, Rule.Evaluate(e));
} // using
}

[TestMethod]
public void IsOffScreenChild_Evaluate_Pass()
{
using (var e = new MockA11yElement())
using (var parent = new MockA11yElement())
{
parent.IsOffScreen = false;
e.Parent = parent;
Assert.AreEqual(EvaluationCode.Pass, Rule.Evaluate(e));
} // using
}

[TestMethod]
public void IsOffScreenChild_Evaluate_ThrowsArgumentNullException()
{
var ex = Assert.ThrowsException<ArgumentNullException>(() => Rule.Evaluate(null));
Assert.AreEqual("e", ex.ParamName);
}
} // class
} // namespace

0 comments on commit 9524eab

Please sign in to comment.