Mobile Accessibility Testing 16June PR#4231
Conversation
WalkthroughThis update introduces comprehensive support for mobile accessibility testing alongside existing web functionality. It adds mobile-specific accessibility standards, rule filtering, and analysis logic. User interfaces are updated to allow selection between web and mobile rules. The mobile analysis engine parses app UI, applies configurable rules, and generates detailed reports. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI
participant ActAccessibilityTesting
participant Analyzer (Web/Mobile)
participant Driver (Selenium/Appium)
participant Report
User->>UI: Selects "Web" or "Mobile" rule type
UI->>ActAccessibilityTesting: Sets CurrentRuleType
User->>Driver: Triggers accessibility test action
Driver->>ActAccessibilityTesting: RunAction (platform type)
ActAccessibilityTesting->>Analyzer: AnalyzerAccessibility (Web) or AnalyzerMobileAccessibility (Mobile)
Analyzer->>Driver: Interact with page/app elements
Analyzer->>Report: Generate accessibility issues and report
Report-->>User: Presents HTML report artifact
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate Unit Tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 21
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/AccessibilityTestResources/MobileAccessiblityRules.jsonis excluded by!**/*.json
📒 Files selected for processing (8)
Ginger/Ginger/Actions/ActionEditPages/ActAccessibilityTestingEditPage.xaml.cs(2 hunks)Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs(2 hunks)Ginger/GingerCoreCommon/External/Configurations/AccessibilityRuleData.cs(2 hunks)Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs(7 hunks)Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/GenericAppiumDriver.cs(7 hunks)Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/MobileAccessibilityAnalyzer.cs(1 hunks)Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs(4 hunks)Ginger/GingerCoreNET/GingerCoreNET.csproj(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/GenericAppiumDriver.cs (1)
undefined
<retrieved_learning>
Learnt from: GokulBothe99
PR: #4188
File: Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs:22-23
Timestamp: 2025-04-25T13:29:45.059Z
Learning: The using Amdocs.Ginger.Repository; statement in Ginger/GingerCoreNET/RunLib/CLILib/DoOptionsHanlder.cs is necessary as it provides access to the ObservableList<> class which is used throughout the file for collections of RunSetConfig, AnalyzerItemBase, and ApplicationPOMModel objects.
</retrieved_learning>
🧬 Code Graph Analysis (1)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (1)
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (1)
AnalyzerAccessibility(935-982)
🔇 Additional comments (2)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (1)
1913-1917: Verify that the correct driver instance is passed for mobile runs
AnalyzerAccessibilityis still called with theDriverfield, notMobDriver.
IfDriveris never reassigned to the Appium instance, the analysis for a mobile platform will silently operate on the wrong driver and fail.Please confirm that, when
act.Platform == ePlatformType.Mobile, either
Driveralready references the Appium driver, or- this call should be changed to
MobDriver.Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (1)
545-557: Embedded-resource filename likely misspelled
AccessiblityRules.json/MobileAccessiblityRules.jsonare missing the second “i” in “Accessibility”. Double-check the actual resource names; a mismatch will return an empty string and later JSON parsing fails silently.
| <ItemGroup> | ||
| <EmbeddedResource Include="Drivers\CoreDrivers\Web\Selenium\AccessibilityTestResources\MobileAccessiblityRules.json"> | ||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| </EmbeddedResource> | ||
| </ItemGroup> |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Duplicate ItemGroup adds noise – embed the new JSON alongside the existing rules
You already have an <ItemGroup> that embeds web rules and related assets (lines 195-226). Adding a second group here for the mobile JSON makes the project file harder to maintain.
- <ItemGroup>
- <EmbeddedResource Include="Drivers\CoreDrivers\Web\Selenium\AccessibilityTestResources\MobileAccessiblityRules.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </EmbeddedResource>
- </ItemGroup>
+ <!-- append to the earlier EmbeddedResource group (around line 195)… -->
+ <EmbeddedResource Include="Drivers\CoreDrivers\Web\Selenium\AccessibilityTestResources\MobileAccessibilityRules.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </EmbeddedResource>Collapsing these groups keeps related resources together and avoids accidental duplication of MSBuild metadata.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/GingerCoreNET.csproj around lines 1319 to 1323, there is
a duplicate <ItemGroup> for embedding the MobileAccessiblityRules.json resource.
To fix this, move the MobileAccessiblityRules.json entry into the existing
<ItemGroup> that contains the web rules and related assets (lines 195-226) so
all related embedded resources are grouped together, eliminating the duplicate
<ItemGroup> and improving maintainability.
| // NEW PROPERTIES: | ||
| public string SuggestedFix { get; set; } | ||
| public string RelatedWCAG { get; set; } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
New properties are not tracked nor serialized
SuggestedFix and RelatedWCAG are plain auto-properties, unlike the rest of the class which:
- Fires
OnPropertyChanged, enabling UI binding. - Is decorated with
[IsSerializedForLocalRepository], ensuring persistence.
Lack of either will cause the values to be lost between sessions and not refresh data-bound grids.
- // NEW PROPERTIES:
- public string SuggestedFix { get; set; }
- public string RelatedWCAG { get; set; }
+ private string mSuggestedFix;
+ [IsSerializedForLocalRepository]
+ public string SuggestedFix
+ {
+ get => mSuggestedFix;
+ set { if (mSuggestedFix != value) { mSuggestedFix = value; OnPropertyChanged(nameof(SuggestedFix)); } }
+ }
+
+ private string mRelatedWCAG;
+ [IsSerializedForLocalRepository]
+ public string RelatedWCAG
+ {
+ get => mRelatedWCAG;
+ set { if (mRelatedWCAG != value) { mRelatedWCAG = value; OnPropertyChanged(nameof(RelatedWCAG)); } }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // NEW PROPERTIES: | |
| public string SuggestedFix { get; set; } | |
| public string RelatedWCAG { get; set; } | |
| private string mSuggestedFix; | |
| [IsSerializedForLocalRepository] | |
| public string SuggestedFix | |
| { | |
| get => mSuggestedFix; | |
| set | |
| { | |
| if (mSuggestedFix != value) | |
| { | |
| mSuggestedFix = value; | |
| OnPropertyChanged(nameof(SuggestedFix)); | |
| } | |
| } | |
| } | |
| private string mRelatedWCAG; | |
| [IsSerializedForLocalRepository] | |
| public string RelatedWCAG | |
| { | |
| get => mRelatedWCAG; | |
| set | |
| { | |
| if (mRelatedWCAG != value) | |
| { | |
| mRelatedWCAG = value; | |
| OnPropertyChanged(nameof(RelatedWCAG)); | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In Ginger/GingerCoreCommon/External/Configurations/AccessibilityRuleData.cs
around lines 62 to 65, the new properties SuggestedFix and RelatedWCAG are plain
auto-properties and do not fire OnPropertyChanged nor have the
[IsSerializedForLocalRepository] attribute. To fix this, modify these properties
to call OnPropertyChanged in their setters to support UI binding and add the
[IsSerializedForLocalRepository] attribute to ensure they are serialized and
persisted correctly.
| xAccessibilityRulesGrid.AddRadioButton(content: "Web", "AccessibilityType", LoadWebRulesData, isChecked: true); | ||
| xAccessibilityRulesGrid.AddRadioButton(content: "Mobile", "AccessibilityType", LoadMobileRulesData); | ||
|
|
||
| xAccessibilityRulesGrid.AddLabel(" "); | ||
|
|
||
| xAccessibilityRulesGrid.AddLabel("Note: Ginger will only analyze the active accessibility testing rules; inactive rules will not be considered."); |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
UI controls belong in XAML, not runtime code
Adding radio buttons and spacer labels through imperative code clutters the constructor and makes future styling harder. Declare them once in the XAML and bind the Checked event to LoadWebRulesData/LoadMobileRulesData.
🤖 Prompt for AI Agents
In Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs around lines 101
to 106, the radio buttons and spacer labels are being added programmatically,
which clutters the constructor and complicates styling. Move these UI control
declarations into the corresponding XAML file instead. Define the radio buttons
and labels in XAML and bind their Checked events to the LoadWebRulesData and
LoadMobileRulesData methods to maintain separation of concerns and improve
maintainability.
| // Inside your AnalyzerMobileAccessibility method in ActAccessibilityTesting | ||
| public void AnalyzerMobileAccessibility(IWebDriver Driver, IWebElement element, Act currentAct, MobileAccessibilityAnalyzer mobileAnalyzer) | ||
| { | ||
| try | ||
| { | ||
| currentAct.Artifacts = new ObservableList<ArtifactDetails>(); // Initialize artifacts list | ||
| currentAct.Status = Amdocs.Ginger.CoreNET.Execution.eRunStatus.Running; | ||
|
|
||
| if (currentAct.Status == Amdocs.Ginger.CoreNET.Execution.eRunStatus.Failed && !string.IsNullOrEmpty(currentAct.Error)) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Self-reference parameter makes the API awkward
AnalyzerMobileAccessibility is an instance method yet it receives MobileAccessibilityAnalyzer mobileAnalyzer and is always invoked with this. The extra parameter is redundant and encourages misuse.
-public void AnalyzerMobileAccessibility(IWebDriver Driver, IWebElement element, Act currentAct, MobileAccessibilityAnalyzer mobileAnalyzer)
+public void AnalyzerMobileAccessibility(IWebDriver driver, IWebElement element, Act currentAct)Inside, use this instead of mobileAnalyzer.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/MobileAccessibilityAnalyzer.cs
around lines 618 to 626, the instance method AnalyzerMobileAccessibility
unnecessarily takes a MobileAccessibilityAnalyzer parameter named
mobileAnalyzer, which is always passed as this. Remove the mobileAnalyzer
parameter from the method signature and update the method body to use this
instead of mobileAnalyzer to simplify the API and avoid redundancy.
| public void AnalyzerMobileAccessibility(IWebDriver driver, IWebElement elementXPath = null) | ||
| { | ||
| try | ||
| { | ||
| Artifacts = []; | ||
| MobileAccessibilityAnalyzer axeBuilder = new MobileAccessibilityAnalyzer(driver, ActiveRulesForAnalysis); | ||
| axeBuilder.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this, axeBuilder); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Confusing builder usage in mobile analyzer invocation
You instantiate MobileAccessibilityAnalyzer as axeBuilder and then pass it into its own instance method. After refactoring the method signature (see earlier comment) the call should become:
var analyzer = new MobileAccessibilityAnalyzer(driver, ActiveRulesForAnalysis);
analyzer.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this);This avoids the double reference and improves readability.
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs around
lines 918 to 925, the method creates a MobileAccessibilityAnalyzer instance
named axeBuilder and then passes this same instance into its own
AnalyzerMobileAccessibility method, which is confusing. To fix this, rename the
variable to analyzer and remove the redundant parameter so the method call is
analyzer.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this)
without passing the analyzer itself. This simplifies the call and improves
readability.
| // Public property to set the type of rules to fetch | ||
| public string CurrentRuleType { get; set; } = "Web"; // Default to "Web" | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Use an enum instead of magic strings for CurrentRuleType
Storing "Web" / "Mobile" as raw strings invites typos and makes switch/if brittle. Define an enum (RulePlatformType) and keep the property strongly-typed.
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs around
lines 66 to 68, replace the string property CurrentRuleType with a
strongly-typed enum named RulePlatformType that defines possible values like Web
and Mobile. Define the enum in an appropriate namespace or file, then change the
CurrentRuleType property type to this enum and set its default value to
RulePlatformType.Web. This eliminates magic strings and reduces errors from
typos in rule type usage.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (3)
66-68: 🛠️ Refactor suggestionUse an enum instead of magic strings for
CurrentRuleTypeThe string property invites typos and makes switch/if statements brittle. Define a strongly-typed enum to represent the platform type.
Define an enum and update the property:
+public enum RulePlatformType +{ + Web, + Mobile +} + // Public property to set the type of rules to fetch -public string CurrentRuleType { get; set; } = "Web"; // Default to "Web" +public RulePlatformType CurrentRuleType { get; set; } = RulePlatformType.Web;Then update all string comparisons throughout the file to use the enum.
918-925: 🛠️ Refactor suggestionConfusing builder usage in mobile analyzer invocation
The method creates a
MobileAccessibilityAnalyzerinstance namedaxeBuilderand then passes this same instance into its ownAnalyzerMobileAccessibilitymethod, which is confusing.Rename the variable and simplify the call:
public void AnalyzerMobileAccessibility(IWebDriver driver, IWebElement elementXPath = null) { try { Artifacts = []; - MobileAccessibilityAnalyzer axeBuilder = new MobileAccessibilityAnalyzer(driver, ActiveRulesForAnalysis); - axeBuilder.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this, axeBuilder); + var analyzer = new MobileAccessibilityAnalyzer(driver, ActiveRulesForAnalysis); + analyzer.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this); } catch (Exception ex) { Error = "Error during mobile accessibility testing: " + ex.Message; Reporter.ToLog(eLogLevel.ERROR, "Error during mobile accessibility testing", ex); } }Note: This assumes the
MobileAccessibilityAnalyzer.AnalyzerMobileAccessibilitymethod signature will be updated to not require passing the analyzer instance to itself.
934-936: 🛠️ Refactor suggestion
platformTypeparameter is unused
AnalyzerAccessibility(IWebDriver Driver, IWebElement element, ePlatformType platformType)ignoresplatformType, risking divergence between caller intent and behaviour.Either remove the unused parameter:
-public void AnalyzerAccessibility(IWebDriver Driver, IWebElement element, ePlatformType platformType) +public void AnalyzerAccessibility(IWebDriver Driver, IWebElement element)Or implement platform-specific logic if the parameter is intended for future use.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs(16 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (6)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (39)
Act(1465-1523)Act(1525-1532)Act(1534-1542)Act(1544-1551)Act(1553-1560)Act(1617-1623)Act(1625-1633)List(2295-2346)List(4892-4967)List(5206-5240)List(6032-6053)List(6123-6175)List(8283-8295)List(10156-10159)List(10203-10222)List(10296-10303)List(10305-10366)List(10711-10714)List(10736-10739)ObservableList(5315-5398)ObservableList(6551-6679)ObservableList(6839-6950)ObservableList(6952-7044)ObservableList(8311-8314)ObservableList(10689-10692)IWebDriver(499-502)IWebElement(4163-4234)IWebElement(4239-4332)IWebElement(4374-4508)IWebElement(4510-4636)IWebElement(4637-4688)IWebElement(4690-4732)IWebElement(4734-4783)IWebElement(4785-4821)IWebElement(4822-4854)IWebElement(4858-4890)IWebElement(5413-5426)IWebElement(9795-9818)IWebElement(10550-10556)Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/MobileAccessibilityAnalyzer.cs (5)
List(43-142)List(196-616)AnalyzerMobileAccessibility(619-686)MobileAccessibilityAnalyzer(19-787)MobileAccessibilityAnalyzer(37-41)Ginger/GingerCoreCommon/Actions/Act.cs (4)
AddOrUpdateInputParamValue(825-848)AddArtifactToAction(1119-1133)AddOrUpdateReturnParamActual(1305-1332)AddOrUpdateReturnParamActualWithPath(1356-1384)Ginger/GingerCoreCommon/External/Configurations/AccessibilityRuleData.cs (2)
ObservableList(66-82)AccessibilityRuleData(27-89)Ginger/GingerCoreNET/WorkSpaceLib/WorkSpace.cs (1)
WorkSpace(61-1019)Ginger/GingerCoreCommon/ReporterLib/Reporter.cs (1)
Reporter(28-357)
🔇 Additional comments (4)
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (4)
69-80: LGTM!The Platforms property correctly adds support for both Web and Mobile platforms.
189-212: LGTM!The
eMobileAccessibilityStandardsenum provides a comprehensive set of mobile accessibility standards with clear descriptions.
1135-1149: LGTM!The
AccessibilityIssueclass is well-structured with appropriate properties for representing accessibility issues.
1153-1154: Consider keepingReadEmbeddedFileinternalChanging the method from
internaltopublicexposes implementation details. If this method needs to be accessed from outside the assembly, consider creating a more specific public API instead.Is there a specific reason this method needs to be public? If not, consider keeping it internal to maintain proper encapsulation.
| private ObservableList<AccessibilityRuleData> GetFilteredRuleList() | ||
| { | ||
| ObservableList<AccessibilityRuleData> allRules = GetRuleList(); | ||
|
|
||
|
|
||
| AccessibilityConfiguration mAccessibilityConfiguration; | ||
| if (WorkSpace.Instance.SolutionRepository != null) | ||
| { | ||
| mAccessibilityConfiguration = WorkSpace.Instance.SolutionRepository.GetFirstRepositoryItem<AccessibilityConfiguration>() ?? new AccessibilityConfiguration(); | ||
| } | ||
| else | ||
| { | ||
| mAccessibilityConfiguration = new AccessibilityConfiguration(); | ||
| } | ||
|
|
||
| // Start with the rules excluded by the user's configuration ('Active' field being false) | ||
| HashSet<string> finalExcludedRuleIds = new HashSet<string>( | ||
| allRules.Where(x => !x.Active).Select(x => x.RuleID), | ||
| StringComparer.OrdinalIgnoreCase | ||
| ); | ||
|
|
||
| // Apply filtering based on Analyzer mode | ||
| if (GetInputParamValue(ActAccessibilityTesting.Fields.Analyzer) == nameof(eAnalyzer.ByStandard)) | ||
| { | ||
| if (StandardList == null || !StandardList.Any()) | ||
| { | ||
|
|
||
| Reporter.ToLog(eLogLevel.ERROR, "Error: 'ByStandard' analyzer selected, but no standards are provided."); | ||
|
|
||
| return new ObservableList<AccessibilityRuleData>(); | ||
| } | ||
|
|
||
|
|
||
| HashSet<string> selectedStandardTags = new HashSet<string>( | ||
| StandardList.Select(item => item.Value.ToString().Equals("bestpractice", StringComparison.OrdinalIgnoreCase) ? "best-practice" : item.Value.ToString()), | ||
| StringComparer.OrdinalIgnoreCase | ||
| ); | ||
|
|
||
| // Assuming Tag is a string or list of strings that can be matched. | ||
| allRules = new ObservableList<AccessibilityRuleData>( | ||
| allRules.Where(r => | ||
| { | ||
| if (string.IsNullOrEmpty(r.Tags)) // Handle cases where Tags might be null or empty | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // Split the comma-separated tags string into a collection of individual tags | ||
| // .Select(s => s.Trim()) to remove leading/trailing whitespace from each tag | ||
| // .ToList() to make it a List<string> if needed, or keep as IEnumerable<string> | ||
| IEnumerable<string> ruleIndividualTags = r.Tags.Split(',') | ||
| .Select(s => s.Trim()) | ||
| .Where(s => !string.IsNullOrWhiteSpace(s)); // Filter out empty strings from splitting | ||
|
|
||
| // Check if any of the individual tags for the rule are in the selectedStandardTags | ||
| return ruleIndividualTags.Any(ruleTag => selectedStandardTags.Contains(ruleTag)); | ||
| }) | ||
| ); | ||
| if (SeverityList != null && SeverityList.Any()) | ||
| { | ||
| List<string> selectedSeverities = SeverityList.Select(x => x.Value.ToLower()).ToList(); | ||
|
|
||
|
|
||
| // Rules to exclude based on severity: if a rule's impact is NOT in the selected severities | ||
| var severityExcludedRuleIds = allRules | ||
| .Where(r => !selectedSeverities.Contains(r.Impact.ToLower())) // Exclude if its Impact is NOT among the selected | ||
| .Select(r => r.RuleID); | ||
|
|
||
| foreach (var ruleId in severityExcludedRuleIds) | ||
| { | ||
| finalExcludedRuleIds.Add(ruleId); | ||
| } | ||
| } | ||
| } | ||
| else if (GetInputParamValue(ActAccessibilityTesting.Fields.Analyzer) == nameof(eAnalyzer.BySeverity)) | ||
| { | ||
| if (SeverityList == null || !SeverityList.Any()) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, "Error: 'BySeverity' analyzer selected, but no severities are provided."); | ||
| // Optionally, set currentAct.Status = Failed and currentAct.Error | ||
| return new ObservableList<AccessibilityRuleData>(); | ||
| } | ||
|
|
||
| List<string> selectedSeverities = SeverityList.Select(x => x.Value.ToLower()).ToList(); | ||
|
|
||
| // Filter rules: only include rules whose Impact matches a selected severity | ||
| allRules = new ObservableList<AccessibilityRuleData>( | ||
| allRules.Where(r => selectedSeverities.Contains(r.Impact.ToLower())) | ||
| ); | ||
| } | ||
|
|
||
| // Final step: Apply the combined exclusions to the initially loaded rules | ||
| // Create the final list of rules that are truly active for the analyzer | ||
| ObservableList<AccessibilityRuleData> finalActiveRules = new ObservableList<AccessibilityRuleData>(); | ||
| foreach (AccessibilityRuleData ruleData in allRules) | ||
| { | ||
| if (!finalExcludedRuleIds.Contains(ruleData.RuleID)) | ||
| { | ||
| ruleData.Active = true; // Mark as active for consistency, though not strictly needed here | ||
| finalActiveRules.Add(ruleData); | ||
| } | ||
| else | ||
| { | ||
| ruleData.Active = false; // Explicitly mark as inactive if excluded | ||
| } | ||
| } | ||
|
|
||
| return finalActiveRules; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Consider refactoring this method for better maintainability
The GetFilteredRuleList method is complex with deeply nested logic. Consider extracting the filtering logic into separate methods for better readability and testability.
Extract the filtering logic into focused methods:
private ObservableList<AccessibilityRuleData> GetFilteredRuleList()
{
ObservableList<AccessibilityRuleData> allRules = GetRuleList();
AccessibilityConfiguration mAccessibilityConfiguration = GetAccessibilityConfiguration();
HashSet<string> finalExcludedRuleIds = new HashSet<string>(
allRules.Where(x => !x.Active).Select(x => x.RuleID),
StringComparer.OrdinalIgnoreCase
);
// Apply filtering based on Analyzer mode
if (GetInputParamValue(ActAccessibilityTesting.Fields.Analyzer) == nameof(eAnalyzer.ByStandard))
{
- if (StandardList == null || !StandardList.Any())
- {
- Reporter.ToLog(eLogLevel.ERROR, "Error: 'ByStandard' analyzer selected, but no standards are provided.");
- return new ObservableList<AccessibilityRuleData>();
- }
-
- HashSet<string> selectedStandardTags = new HashSet<string>(
- StandardList.Select(item => item.Value.ToString().Equals("bestpractice", StringComparison.OrdinalIgnoreCase) ? "best-practice" : item.Value.ToString()),
- StringComparer.OrdinalIgnoreCase
- );
-
- // Assuming Tag is a string or list of strings that can be matched.
- allRules = new ObservableList<AccessibilityRuleData>(
- allRules.Where(r =>
- {
- if (string.IsNullOrEmpty(r.Tags))
- {
- return false;
- }
-
- IEnumerable<string> ruleIndividualTags = r.Tags.Split(',')
- .Select(s => s.Trim())
- .Where(s => !string.IsNullOrWhiteSpace(s));
-
- return ruleIndividualTags.Any(ruleTag => selectedStandardTags.Contains(ruleTag));
- })
- );
-
- if (SeverityList != null && SeverityList.Any())
- {
- List<string> selectedSeverities = SeverityList.Select(x => x.Value.ToLower()).ToList();
-
- var severityExcludedRuleIds = allRules
- .Where(r => !selectedSeverities.Contains(r.Impact.ToLower()))
- .Select(r => r.RuleID);
-
- foreach (var ruleId in severityExcludedRuleIds)
- {
- finalExcludedRuleIds.Add(ruleId);
- }
- }
+ allRules = FilterByStandard(allRules, finalExcludedRuleIds);
}
else if (GetInputParamValue(ActAccessibilityTesting.Fields.Analyzer) == nameof(eAnalyzer.BySeverity))
{
- if (SeverityList == null || !SeverityList.Any())
- {
- Reporter.ToLog(eLogLevel.ERROR, "Error: 'BySeverity' analyzer selected, but no severities are provided.");
- return new ObservableList<AccessibilityRuleData>();
- }
-
- List<string> selectedSeverities = SeverityList.Select(x => x.Value.ToLower()).ToList();
-
- allRules = new ObservableList<AccessibilityRuleData>(
- allRules.Where(r => selectedSeverities.Contains(r.Impact.ToLower()))
- );
+ allRules = FilterBySeverity(allRules);
}
return CreateFinalActiveRules(allRules, finalExcludedRuleIds);
}
+
+private ObservableList<AccessibilityRuleData> FilterByStandard(ObservableList<AccessibilityRuleData> rules, HashSet<string> excludedRuleIds)
+{
+ // Implementation here
+}
+
+private ObservableList<AccessibilityRuleData> FilterBySeverity(ObservableList<AccessibilityRuleData> rules)
+{
+ // Implementation here
+}
+
+private string NormalizeStandardTag(string tag)
+{
+ return tag.Equals("bestpractice", StringComparison.OrdinalIgnoreCase) ? "best-practice" : tag;
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs around
lines 435 to 543, the GetFilteredRuleList method contains complex and deeply
nested filtering logic. Refactor by extracting distinct filtering steps—such as
filtering by standard tags, filtering by severity, and applying exclusions—into
separate private methods. Then call these methods sequentially from
GetFilteredRuleList to improve readability, maintainability, and testability.
| private static string GetAccessiblityrules(string platType) | ||
| { | ||
| try | ||
| { | ||
| if (platType.Equals("Mobile", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return EmbeddedResourceProvider.ReadEmbeddedFile("MobileAccessiblityRules.json"); | ||
| } | ||
| else if (platType.Equals("Web", StringComparison.OrdinalIgnoreCase)) | ||
| { | ||
| return EmbeddedResourceProvider.ReadEmbeddedFile("AccessiblityRules.json"); | ||
| } | ||
| return string.Empty; | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Reporter.ToLog(eLogLevel.ERROR, "Error reading accessibility rules from embedded file", ex); | ||
| return string.Empty; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use enum for platform type and fix parameter name typo
The method uses string comparison which is brittle, and the parameter name has a typo.
Update to use the enum suggested earlier:
-private static string GetAccessiblityrules(string platType)
+private static string GetAccessibilityRules(RulePlatformType platformType)
{
try
{
- if (platType.Equals("Mobile", StringComparison.OrdinalIgnoreCase))
+ if (platformType == RulePlatformType.Mobile)
{
return EmbeddedResourceProvider.ReadEmbeddedFile("MobileAccessiblityRules.json");
}
- else if (platType.Equals("Web", StringComparison.OrdinalIgnoreCase))
+ else if (platformType == RulePlatformType.Web)
{
return EmbeddedResourceProvider.ReadEmbeddedFile("AccessiblityRules.json");
}
return string.Empty;
}
catch (Exception ex)
{
Reporter.ToLog(eLogLevel.ERROR, "Error reading accessibility rules from embedded file", ex);
return string.Empty;
}
}Also fix the typo in the JSON filename: MobileAccessiblityRules.json → MobileAccessibilityRules.json
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs between
lines 545 and 564, replace the string parameter platType with the appropriate
enum type for platform to avoid brittle string comparisons. Update the method to
use enum comparisons instead of string.Equals. Also, fix the typo in the JSON
filename by changing "MobileAccessiblityRules.json" to
"MobileAccessibilityRules.json" to ensure the correct file is read.
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs (1)
51-52: Replace ternary with the idiomatic null-coalescing assignmentThe same list instance can be preserved more succinctly and safely:
- mAccessibilityConfiguration.ExcludedRules = mAccessibilityConfiguration.ExcludedRules != null ? mAccessibilityConfiguration.ExcludedRules : []; + mAccessibilityConfiguration.ExcludedRules ??= new();Benefits: shorter, clearer, avoids evaluating the property twice.
♻️ Duplicate comments (11)
Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs (1)
101-105: UI elements should live in XAML, not be spawned in code-behind
This segment repeats the pattern previously flagged – constructing radio buttons and spacer labels imperatively. Moving these controls to the.xamlkeeps presentation separate from logic, simplifies styling, and eliminates noisy constructor code.Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (3)
50-50: Remove unsupported namespaceOpenQA.Selenium.Common
This namespace does not exist in Selenium 4.x and will break compilation. Remove it entirely or replace with the correct support namespace (OpenQA.Selenium.Support.UI) if needed.
405-405: Specify generic type forAppiumDriver
AppiumDriveris a generic abstract class (AppiumDriver<TWebElement>). Update the field declaration to include the generic parameter for compatibility and to avoid CS0308.Apply this diff:
-protected AppiumDriver MobDriver; +protected AppiumDriver<IWebElement> MobDriver;
3511-3518: Correct exception message and improve JS attribute setting
Fix the typo in the exception text and switch tosetAttributefor robust handling of all attribute names (including those with hyphens).Use this patch:
- if (vals.Length != 2) - { - throw new Exception(@"Inot string should be in the format : attribute=value"); - } ((IJavaScriptExecutor)Driver).ExecuteScript("arguments[0]." + vals[0] + "=arguments[1]", e, vals[1]); + if (vals.Length != 2) + { + throw new Exception(@"Input string should be in the format: attribute=value"); + } + ((IJavaScriptExecutor)Driver) + .ExecuteScript( + "arguments[0].setAttribute(arguments[1], arguments[2]);", + e, + vals[0].Trim(), + vals[1] + );Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/MobileAccessibilityAnalyzer.cs (4)
26-29: Hard-coded viewport size still present – breaks off-screen detection
ConstantsSCREEN_WIDTH_PX/SCREEN_HEIGHT_PXremain fixed at 1080×2220. Any device with a different resolution will mis-reportElement_OffScreen/Element_PartiallyOffScreen. Retrieve the size once from the driver (((IHasSize)_driver).Size) and use those values.
455-468: Logic re-uses the same hard-coded dimensions
The off-screen check here relies on the constants above, so it inherits the same inaccuracy. Once the constants are removed, update this block to use the runtime size variables.
760-766: Dynamic strings injected into HTML without encoding
issue.Description,issue.ElementIdentifier, andissue.SuggestedFixare interpolated directly, risking broken or malicious HTML. Run each throughHttpUtility.HtmlEncode(or equivalent) before appending.
624-632: RedundantmobileAnalyzerparameter still here
AnalyzerMobileAccessibilityis an instance method but still receives aMobileAccessibilityAnalyzerparameter that is alwaysthis. Drop the parameter and fix callers to remove the self-passing.Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (3)
66-68: Use an enum instead of string forCurrentRuleType
Storing"Web"/"Mobile"as raw strings invites typos. Replace with a strongly-typed enum (e.g.,RulePlatformType) and update usages.
534-544: Filename typo & brittle string comparison remain
"MobileAccessiblityRules.json"is still misspelled; it should be"MobileAccessibilityRules.json".
Also, string comparisons onplatTypeare brittle—switch to the suggested enum andswitch/case.
907-915: Self-passing of analyzer instance makes the call confusing
axeBuilder.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this, axeBuilder);passes the analyzer into its own method. After removing the redundant parameter, the call should simply beanalyzer.AnalyzerMobileAccessibility(driver, elementXPath, currentAct: this);.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs(2 hunks)Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs(8 hunks)Ginger/GingerCoreNET/Drivers/CoreDrivers/Mobile/Appium/MobileAccessibilityAnalyzer.cs(1 hunks)Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs(7 hunks)
🧰 Additional context used
🧠 Learnings (2)
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (1)
undefined
<retrieved_learning>
Learnt from: prashelke
PR: #4232
File: Ginger/GingerCoreNET/ActionsLib/UI/VisualTesting/VRTAnalyzer.cs:19-22
Timestamp: 2025-06-16T10:37:13.024Z
Learning: In Ginger codebase, both using amdocs.ginger.GingerCoreNET; and using Amdocs.Ginger.CoreNET; are valid and serve different purposes. The first (lowercase) contains the WorkSpace class and related workspace functionality, while the second (proper case) contains drivers like GenericAppiumDriver and other core functionality. Both may be required in files that use types from both namespaces.
</retrieved_learning>
Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (2)
Learnt from: prashelke
PR: Ginger-Automation/Ginger#4232
File: Ginger/GingerCoreNET/ActionsLib/UI/VisualTesting/VRTAnalyzer.cs:19-22
Timestamp: 2025-06-16T10:37:13.024Z
Learning: In Ginger codebase, both `using amdocs.ginger.GingerCoreNET;` and `using Amdocs.Ginger.CoreNET;` are valid and serve different purposes. The first (lowercase) contains the WorkSpace class and related workspace functionality, while the second (proper case) contains drivers like GenericAppiumDriver and other core functionality. Both may be required in files that use types from both namespaces.
Learnt from: prashelke
PR: Ginger-Automation/Ginger#4232
File: Ginger/GingerCoreNET/ActionsLib/UI/VisualTesting/VRTAnalyzer.cs:19-22
Timestamp: 2025-06-16T09:52:40.854Z
Learning: The `GenericAppiumDriver` class is located in the `Amdocs.Ginger.CoreNET` namespace, not in a namespace that matches its file path structure. File paths don't always correspond to namespace declarations in this codebase.
🔇 Additional comments (3)
Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs (1)
101-105:CurrentRuleTypenever initialised on first renderBecause the default-checked “Web” radio button is selected before any user interaction, the
LoadWebMobileRulesDatahandler is never fired andactAccessibilityTesting.CurrentRuleTypemay staynull/empty. Manually set it after adding the buttons or trigger the handler once:actAccessibilityTesting.CurrentRuleType = "Web"; // after AddRadioButton callsAvoids an initial empty grid or wrong rule filtering.
Ginger/GingerCoreNET/Drivers/CoreDrivers/Web/Selenium/SeleniumDriver.cs (1)
47-47: Verify Appium dependency
Ensure the project references the required Appium .NET client package (e.g.,Appium.WebDriver) so that theOpenQA.Selenium.Appiumnamespace resolves correctly.Run:
#!/bin/bash # Verify Appium.WebDriver package reference in csproj files rg -l "PackageReference.*Appium.WebDriver" -g "*.csproj"Ginger/GingerCoreNET/ActionsLib/UI/Web/ActAccessibilityTesting.cs (1)
1068-1070: 👍 Loop overwrite bug fixed
The newAnycheck correctly replaces the previous loop that overwroteActionResult. Nice catch.
| private void LoadWebMobileRulesData(object sender, RoutedEventArgs e) | ||
| { | ||
| RadioButton clickedRadioButton = sender as RadioButton; | ||
|
|
||
| if (clickedRadioButton != null && clickedRadioButton.IsChecked.HasValue && clickedRadioButton.IsChecked.Value) | ||
| { | ||
| string selectedOption = clickedRadioButton.Content.ToString(); | ||
| actAccessibilityTesting.CurrentRuleType = selectedOption; | ||
| } | ||
| LoadGridData(); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Condense null/checked guards with pattern matching
The handler can be cleaner and safer:
- RadioButton clickedRadioButton = sender as RadioButton;
-
- if (clickedRadioButton != null && clickedRadioButton.IsChecked.HasValue && clickedRadioButton.IsChecked.Value)
- {
- string selectedOption = clickedRadioButton.Content.ToString();
- actAccessibilityTesting.CurrentRuleType = selectedOption;
- }
+ if (sender is RadioButton { IsChecked: true, Content: string option })
+ {
+ actAccessibilityTesting.CurrentRuleType = option;
+ }Fewer null checks, no ToString(), eliminates the extra variable.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private void LoadWebMobileRulesData(object sender, RoutedEventArgs e) | |
| { | |
| RadioButton clickedRadioButton = sender as RadioButton; | |
| if (clickedRadioButton != null && clickedRadioButton.IsChecked.HasValue && clickedRadioButton.IsChecked.Value) | |
| { | |
| string selectedOption = clickedRadioButton.Content.ToString(); | |
| actAccessibilityTesting.CurrentRuleType = selectedOption; | |
| } | |
| LoadGridData(); | |
| } | |
| private void LoadWebMobileRulesData(object sender, RoutedEventArgs e) | |
| { | |
| if (sender is RadioButton { IsChecked: true, Content: string option }) | |
| { | |
| actAccessibilityTesting.CurrentRuleType = option; | |
| } | |
| LoadGridData(); | |
| } |
🤖 Prompt for AI Agents
In Ginger/Ginger/SolutionWindows/AccessibilityRulePage.xaml.cs around lines 119
to 129, refactor the LoadWebMobileRulesData method to use pattern matching for
the sender parameter to directly check and cast it to a checked RadioButton.
Remove explicit null checks and ToString() calls by using pattern matching with
a condition that the RadioButton is checked, and assign the Content property
directly to CurrentRuleType without an intermediate variable. This will simplify
and make the code safer.
Thank you for your contribution.
Before submitting this PR, please make sure:
Summary by CodeRabbit
New Features
Bug Fixes
Chores