From 6e7fefc218903edb9ff0242009f09b63c70dbc0d Mon Sep 17 00:00:00 2001 From: Agency Date: Tue, 31 Mar 2026 14:22:54 +0530 Subject: [PATCH 1/9] Add AccessibilityUnitTest project for rngallery --- windows/rngallery.Package/packages.lock.json | 115 ++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/windows/rngallery.Package/packages.lock.json b/windows/rngallery.Package/packages.lock.json index 4f23ac5e..bd80a1f6 100644 --- a/windows/rngallery.Package/packages.lock.json +++ b/windows/rngallery.Package/packages.lock.json @@ -19,6 +19,34 @@ "Microsoft.WindowsAppSDK.WinUI": "[1.8.260204000]" } }, + "boost": { + "type": "Transitive", + "resolved": "1.83.0", + "contentHash": "cy53VNMzysEMvhBixDe8ujPk67Fcj3v6FPHQnH91NYJNLHpc6jxa2xq9ruCaaJjE4M3YrGSHDi4uUSTGBWw6EQ==" + }, + "Microsoft.JavaScript.Hermes": { + "type": "Transitive", + "resolved": "0.0.0-2512.22001-bc3d0ed7", + "contentHash": "aMuCKrIwkCAnT56+oKqmxgfIaAHlKRVt8IiG/jtMbG01QH1mLPwL7wP89jRMsYSJzikW96trqgpUllZZa3O+Qw==" + }, + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.ReactNative.Cxx": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "V6DvFBFs70fXudh3+Tp0UQ0mroBNw4tvlOZN9TBGqi24dqbjvuKlG/CitDRhh8PG9vw8QwR4gE+I7zJHXYywLw==", + "dependencies": { + "Microsoft.ReactNative": "0.82.0" + } + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -114,16 +142,39 @@ } }, "clipboard": { - "type": "Project" + "type": "Project", + "dependencies": { + "Microsoft.ReactNative": "[0.82.0, )", + "Microsoft.ReactNative.Cxx": "[0.82.0, )", + "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", + "Microsoft.WindowsAppSDK": "[1.8.260209005, )", + "boost": "[1.83.0, )" + } }, "rngallery": { "type": "Project", "dependencies": { - "Clipboard": "[1.0.0, )" + "Clipboard": "[1.0.0, )", + "Microsoft.JavaScript.Hermes": "[0.0.0-2512.22001-bc3d0ed7, )", + "Microsoft.ReactNative": "[0.82.0, )", + "Microsoft.ReactNative.Cxx": "[0.82.0, )", + "Microsoft.VCRTForwarders.140": "[1.0.2-rc, )", + "Microsoft.WindowsAppSDK": "[1.8.260209005, )", + "boost": "[1.83.0, )" } } }, "UAP,Version=v10.0.17763/win10-arm": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -140,6 +191,16 @@ } }, "UAP,Version=v10.0.17763/win10-arm-aot": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -156,6 +217,16 @@ } }, "UAP,Version=v10.0.17763/win10-arm64-aot": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -172,6 +243,16 @@ } }, "UAP,Version=v10.0.17763/win10-x64": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -188,6 +269,16 @@ } }, "UAP,Version=v10.0.17763/win10-x64-aot": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -204,6 +295,16 @@ } }, "UAP,Version=v10.0.17763/win10-x86": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", @@ -220,6 +321,16 @@ } }, "UAP,Version=v10.0.17763/win10-x86-aot": { + "Microsoft.ReactNative": { + "type": "Transitive", + "resolved": "0.82.0", + "contentHash": "a9NTivAEa7ynzw2yZGpLqj+4PdUZs04fNuEm4gfLvj8hZL94eqKY+VBNDzZeTZSdQjsSS/h/Mvoh/2DJkR3kyQ==" + }, + "Microsoft.VCRTForwarders.140": { + "type": "Transitive", + "resolved": "1.0.2-rc", + "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" + }, "Microsoft.Web.WebView2": { "type": "Transitive", "resolved": "1.0.3179.45", From 65bb1b614a2035ca7e55acdd470edc593d8331c8 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Tue, 31 Mar 2026 16:06:31 +0530 Subject: [PATCH 2/9] Accessibility Fast Pass UnitTest --- windows/.gitignore | 6 + .../AccessibilitySnapshot.json | 194 ++++++++++ .../AccessibilityTests.cs | 189 ++++++++++ .../AccessibilityUnitTest.csproj | 124 +++++++ .../AccessibilityUnitTest/GallerySession.cs | 331 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 20 ++ windows/AccessibilityUnitTest/app.config | 11 + windows/AccessibilityUnitTest/packages.config | 13 + windows/rngallery.sln | 68 +++- 9 files changed, 942 insertions(+), 14 deletions(-) create mode 100644 windows/AccessibilityUnitTest/AccessibilitySnapshot.json create mode 100644 windows/AccessibilityUnitTest/AccessibilityTests.cs create mode 100644 windows/AccessibilityUnitTest/AccessibilityUnitTest.csproj create mode 100644 windows/AccessibilityUnitTest/GallerySession.cs create mode 100644 windows/AccessibilityUnitTest/Properties/AssemblyInfo.cs create mode 100644 windows/AccessibilityUnitTest/app.config create mode 100644 windows/AccessibilityUnitTest/packages.config diff --git a/windows/.gitignore b/windows/.gitignore index fcee016b..74963be2 100644 --- a/windows/.gitignore +++ b/windows/.gitignore @@ -44,3 +44,9 @@ Ankh.NoLoad *.binlog *.err *.wrn + +# Accessibility scan results +AccessibilityUnitTest/ScanResults/ + +# NuGet packages +packages/ diff --git a/windows/AccessibilityUnitTest/AccessibilitySnapshot.json b/windows/AccessibilityUnitTest/AccessibilitySnapshot.json new file mode 100644 index 00000000..ca95c678 --- /dev/null +++ b/windows/AccessibilityUnitTest/AccessibilitySnapshot.json @@ -0,0 +1,194 @@ +{ + "TotalTests": 17, + "TotalErrors": 16, + "Results": [ + { + "TestName": "TestNavigateToClipboardAndValidate", + "ErrorCount": 1, + "Errors": [ + { + "RuleId": 2, + "Description": "An on-screen element must not have a null BoundingRectangle property.", + "HowToFix": "If the element is off-screen, set its IsOffscreen property to true.\r\nIf the element is on-screen, provide a BoundingRectangle property.", + "Element": "RuntimeId: [2A,40CF8,4,88CE], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + } + ] + }, + { + "TestName": "TestNavigateToTouchableWithoutFeedbackAndValidate", + "ErrorCount": 2, + "Errors": [ + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "RuntimeId: [2A,40CF8,4,D128], BoundingRectangle: [l=-1322,t=1279,r=-1282,b=1329], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Decreases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=40,h=50]" + }, + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "RuntimeId: [2A,40CF8,4,D130], BoundingRectangle: [l=-1212,t=1279,r=-1172,b=1329], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Increases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=40,h=50]" + } + ] + }, + { + "TestName": "TestNavigateToTextAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToButtonAndValidate", + "ErrorCount": 6, + "Errors": [ + { + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "RuntimeId: [2A,40CF8,4,848A], BoundingRectangle: [l=-1300,t=226,r=-1195,b=264], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, HasKeyboardFocus: True, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=105,h=38]" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "RuntimeId: [2A,40CF8,4,848A], BoundingRectangle: [l=-1300,t=226,r=-1195,b=264], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, HasKeyboardFocus: True, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=105,h=38]" + }, + { + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "RuntimeId: [2A,40CF8,4,84C4], BoundingRectangle: [l=-1304,t=413,r=-1191,b=450], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: colored button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=113,h=37]" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "RuntimeId: [2A,40CF8,4,84C4], BoundingRectangle: [l=-1304,t=413,r=-1191,b=450], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: colored button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=113,h=37]" + }, + { + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "RuntimeId: [2A,40CF8,4,84FE], BoundingRectangle: [l=-1306,t=600,r=-1189,b=637], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=117,h=37]" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "RuntimeId: [2A,40CF8,4,84FE], BoundingRectangle: [l=-1306,t=600,r=-1189,b=637], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=117,h=37]" + } + ] + }, + { + "TestName": "TestNavigateToScrollViewAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToImageAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToFlatListAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToTouchableOpacityAndValidate", + "ErrorCount": 2, + "Errors": [ + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "RuntimeId: [2A,40CF8,4,C9DC], BoundingRectangle: [l=-1342,t=1452,r=-1292,b=1492], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Decreases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=50,h=40]" + }, + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "RuntimeId: [2A,40CF8,4,C9E6], BoundingRectangle: [l=-1202,t=1452,r=-1152,b=1492], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Increases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=50,h=40]" + } + ] + }, + { + "TestName": "TestNavigateToSettingsAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestZNavigation", + "ErrorCount": 2, + "Errors": [ + { + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "RuntimeId: [2A,40CF8,4,E118], BoundingRectangle: [l=-2544,t=-26,r=-2206,b=8], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=338,h=34]" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "RuntimeId: [2A,40CF8,4,E118], BoundingRectangle: [l=-2544,t=-26,r=-2206,b=8], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=338,h=34]" + } + ] + }, + { + "TestName": "TestNavigateToViewAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToPressableAndValidate", + "ErrorCount": 1, + "Errors": [ + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "RuntimeId: [2A,40CF8,4,A468], BoundingRectangle: [l=-1317,t=429,r=-1177,b=479], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Pressable, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=140,h=50]" + } + ] + }, + { + "TestName": "TestNavigateToTouchableHighlightAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToTextInputAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToSwitchAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToModalAndValidate", + "ErrorCount": 2, + "Errors": [ + { + "RuleId": 2, + "Description": "An on-screen element must not have a null BoundingRectangle property.", + "HowToFix": "If the element is off-screen, set its IsOffscreen property to true.\r\nIf the element is on-screen, provide a BoundingRectangle property.", + "Element": "RuntimeId: [2A,40CF8,4,9E76], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, Name: , HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + }, + { + "RuleId": 133, + "Description": "The Name property must not contain only whitespace.", + "HowToFix": "Provide a UI Automation Name property that concisely identifies the element.", + "Element": "RuntimeId: [2A,40CF8,4,9E76], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, Name: , HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + } + ] + }, + { + "TestName": "TestNavigateToHomeAndValidate", + "ErrorCount": 0, + "Errors": [] + } + ] +} \ No newline at end of file diff --git a/windows/AccessibilityUnitTest/AccessibilityTests.cs b/windows/AccessibilityUnitTest/AccessibilityTests.cs new file mode 100644 index 00000000..7bf4a3fb --- /dev/null +++ b/windows/AccessibilityUnitTest/AccessibilityTests.cs @@ -0,0 +1,189 @@ +using AccessibilityUnitTest; +using Axe.Windows.Automation; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Windows.Automation; + +namespace AccessibilityUnitTest +{ + [TestClass] + public class AccessibilityTests : GallerySession + { + private static AutomationElement GalleryWindow; + private static int processId; + + public TestContext TestContext { get; set; } + + /// + /// Creates a scanner whose output file is named after the current test. + /// + private IScanner GetTestScanner() + { + return CreateScannerForTest(processId, TestContext.TestName); + } + + [TestMethod] + public void TestZNavigation() + { + // Navigate home first to reset UI state when running after other tests + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + FindAndInvokeElement(GalleryWindow, "Home", waitMs: 1000); + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + var navMenu = FindElement(GalleryWindow, "Navigation menu"); + var parent = GetParentElement(navMenu); + FindAndExpandElement(GalleryWindow, "Basic Input"); + FindAndExpandElement(GalleryWindow, "Collections"); + FindAndExpandElement(GalleryWindow, "Dialogs & flyouts"); + FindAndExpandElement(GalleryWindow, "Layout"); + FindAndExpandElement(GalleryWindow, "Media"); + FindAndExpandElement(GalleryWindow, "Scrolling"); + FindAndExpandElement(GalleryWindow, "Text"); + FindAndExpandElement(GalleryWindow, "System"); + FindAndExpandElement(GalleryWindow, "Legacy"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner(), parent)); + } + + [TestMethod] + public void TestNavigateToHomeAndValidate() + { + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + FindAndInvokeElement(GalleryWindow, "Home"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToSettingsAndValidate() + { + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + FindAndInvokeElement(GalleryWindow, "Settings"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToButtonAndValidate() + { + NavigateToComponent(GalleryWindow, "Button1 control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToPressableAndValidate() + { + NavigateToComponent(GalleryWindow, "Pressable control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToSwitchAndValidate() + { + NavigateToComponent(GalleryWindow, "Switch control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToFlatListAndValidate() + { + NavigateToComponent(GalleryWindow, "FlatList control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + //[TestMethod] + //public void TestNavigateToVirtualizedListAndValidate() + //{ + // NavigateToComponent(GalleryWindow, "VirtualizedList control"); + // AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + //} + + [TestMethod] + public void TestNavigateToModalAndValidate() + { + NavigateToComponent(GalleryWindow, "Modal control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToViewAndValidate() + { + NavigateToComponent(GalleryWindow, "View control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToImageAndValidate() + { + NavigateToComponent(GalleryWindow, "Image control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToScrollViewAndValidate() + { + NavigateToComponent(GalleryWindow, "ScrollView control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToTextAndValidate() + { + NavigateToComponent(GalleryWindow, "Text control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToTextInputAndValidate() + { + NavigateToComponent(GalleryWindow, "TextInput control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToClipboardAndValidate() + { + NavigateToComponent(GalleryWindow, "Clipboard control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToTouchableHighlightAndValidate() + { + NavigateToComponent(GalleryWindow, "TouchableHighlight control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToTouchableOpacityAndValidate() + { + NavigateToComponent(GalleryWindow, "TouchableOpacity control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [TestMethod] + public void TestNavigateToTouchableWithoutFeedbackAndValidate() + { + NavigateToComponent(GalleryWindow, "TouchableWithoutFeedback control"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + try + { + processId = GetProcessId(); + GalleryWindow = InitializeGalleryWindow(); + } + catch (Exception ex) + { + Console.WriteLine($"Error during initialization: {ex.Message}"); + throw; + } + } + + [ClassCleanup] + public static void ClassCleanup() + { + string snapshotPath = WriteSnapshotFile(); + Console.WriteLine($"Snapshot saved to: {snapshotPath}"); + } + } +} diff --git a/windows/AccessibilityUnitTest/AccessibilityUnitTest.csproj b/windows/AccessibilityUnitTest/AccessibilityUnitTest.csproj new file mode 100644 index 00000000..6418426a --- /dev/null +++ b/windows/AccessibilityUnitTest/AccessibilityUnitTest.csproj @@ -0,0 +1,124 @@ + + + + + + Debug + AnyCPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC} + Library + Properties + AccessibilityUnitTest + AccessibilityUnitTest + v4.8.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Actions.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Automation.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Core.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Desktop.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Rules.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.RuleSelection.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.SystemAbstractions.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Telemetry.dll + + + ..\packages\Axe.Windows.2.4.2\lib\netstandard20\Axe.Windows.Win32.dll + + + ..\packages\Interop.UIAutomationClient.10.19041.0\lib\net45\Interop.UIAutomationClient.dll + False + + + ..\packages\MSTest.TestFramework.2.2.10\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.2.2.10\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll + + + ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + + + ..\packages\System.Drawing.Common.8.0.10\lib\net462\System.Drawing.Common.dll + + + ..\packages\System.IO.Packaging.8.0.1\lib\net462\System.IO.Packaging.dll + + + ..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + diff --git a/windows/AccessibilityUnitTest/GallerySession.cs b/windows/AccessibilityUnitTest/GallerySession.cs new file mode 100644 index 00000000..08895689 --- /dev/null +++ b/windows/AccessibilityUnitTest/GallerySession.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Automation; +using Axe.Windows.Automation; +using Axe.Windows.Automation.Data; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace AccessibilityUnitTest +{ + public class GallerySession + { + private const string ApplicationProcessName = "rngallery"; + private const string WindowName = "React Native Gallery"; + + // Snapshot: accumulates per-test scan results + private static readonly ConcurrentDictionary _snapshotResults = + new ConcurrentDictionary(); + private static int _totalSnapshotErrors = 0; + + public static int GetProcessId() + { + var processes = System.Diagnostics.Process.GetProcessesByName(ApplicationProcessName); + return processes[0].Id; + } + + public static IScanner CreateScanner(int processId, string outputDirectory = null) + { + Console.WriteLine("Creating scanner..."); + var configBuilder = Config.Builder.ForProcessId(processId) + .WithOutputFileFormat(OutputFileFormat.A11yTest); + + if (!string.IsNullOrEmpty(outputDirectory)) + { + Directory.CreateDirectory(outputDirectory); + configBuilder.WithOutputDirectory(outputDirectory); + } + + var config = configBuilder.Build(); + var scanner = ScannerFactory.CreateScanner(config); + return scanner; + } + + /// + /// Creates a scanner whose output file goes into a folder named after the test. + /// + public static IScanner CreateScannerForTest(int processId, string testName) + { + string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "ScanResults"); + string testOutputDir = Path.GetFullPath(Path.Combine(baseDir, testName)); + Console.WriteLine($"Scan output directory: {testOutputDir}"); + return CreateScanner(processId, testOutputDir); + } + + public static ScanOutput GetScanResults(IScanner scanner) + { + Console.WriteLine("Scanning..."); + var scanResults = scanner.Scan(null); + return scanResults; + } + + /// + /// Scans a specific element's subtree using its AutomationId. + /// Falls back to a full scan if the element has no AutomationId. + /// + public static ScanOutput GetScanResults(IScanner scanner, AutomationElement element) + { + Console.WriteLine($"Scanning element: {element.Current.Name} (Control Type: {element.Current.ControlType.LocalizedControlType})..."); + + string automationId = element.Current.AutomationId; + if (string.IsNullOrEmpty(automationId)) + { + Console.WriteLine("Element has no AutomationId — performing full scan instead."); + return scanner.Scan(null); + } + + var scanOptions = new ScanOptions(automationId); + var scanResults = scanner.Scan(scanOptions); + return scanResults; + } + + /// + /// Adds the scan results for a test to the in-memory snapshot. + /// + public static void AddTestSnapshot(string testName, ScanOutput scanResults) + { + var errors = new List(); + foreach (var windowOutput in scanResults.WindowScanOutputs) + { + foreach (var error in windowOutput.Errors) + { + errors.Add(new + { + RuleId = error.Rule.ID, + Description = error.Rule.Description, + HowToFix = error.Rule.HowToFix, + Element = error.Element?.Properties != null + ? string.Join(", ", error.Element.Properties.Select(p => $"{p.Key}: {p.Value}")) + : null + }); + } + } + + var result = new + { + TestName = testName, + ErrorCount = errors.Count, + Errors = errors + }; + + _snapshotResults[testName] = result; + System.Threading.Interlocked.Add(ref _totalSnapshotErrors, errors.Count); + Console.WriteLine($"[Snapshot] Recorded {errors.Count} error(s) for '{testName}'"); + } + + /// + /// Writes the accumulated snapshot to a single JSON file. + /// Call from [ClassCleanup]. + /// + public static string WriteSnapshotFile(string outputFileName = "AccessibilitySnapshot.json") + { + string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", ".."); + string snapshotDir = Path.GetFullPath(baseDir); + + string filePath = Path.Combine(snapshotDir, outputFileName); + + var snapshot = new + { + TotalTests = _snapshotResults.Count, + TotalErrors = _totalSnapshotErrors, + Results = _snapshotResults.Values.ToList() + }; + + string json = JsonConvert.SerializeObject(snapshot, Formatting.Indented); + File.WriteAllText(filePath, json); + + Console.WriteLine($"[Snapshot] Written to {filePath}"); + return filePath; + } + + public static bool IsErrorPresent(ScanOutput scanResults) + { + bool isError = false; + foreach (var scanResult in scanResults.WindowScanOutputs) + { + foreach (var error in scanResult.Errors) + { + Console.WriteLine($"- {error.Rule.Description}"); + isError = true; + } + } + return isError; + } + + /// + /// Initializes the gallery window and scanner. Call from [ClassInitialize]. + /// + public static (AutomationElement window, IScanner scanner) InitializeGallerySession() + { + int processId = GetProcessId(); + var scanner = CreateScanner(processId); + var window = InitializeGalleryWindow(); + return (window, scanner); + } + + /// + /// Initializes and returns the gallery window only (no scanner). + /// Use when creating per-test scanners via CreateScannerForTest. + /// + public static AutomationElement InitializeGalleryWindow() + { + AutomationElement desktop = AutomationElement.RootElement; + + Condition windowCondition = new PropertyCondition( + AutomationElement.ControlTypeProperty, + ControlType.Window); + + Condition nameCondition = new PropertyCondition( + AutomationElement.NameProperty, + WindowName); + + Condition condition = new AndCondition(windowCondition, nameCondition); + + var window = desktop.FindFirst(TreeScope.Children, condition); + Assert.IsNotNull(window, $"Window '{WindowName}' was not found"); + + return window; + } + + /// + /// Finds an element by name and returns it. Fails the test if not found. + /// + public static AutomationElement FindElement(AutomationElement parent, string elementName) + { + Condition condition = new PropertyCondition(AutomationElement.NameProperty, elementName); + var element = parent.FindFirst(TreeScope.Subtree, condition); + Assert.IsNotNull(element, $"Element with name '{elementName}' was not found"); + + Console.WriteLine($"Found element: {element.Current.Name}"); + Console.WriteLine($"Control Type: {element.Current.ControlType.LocalizedControlType}"); + + return element; + } + + /// + /// Gets the parent element of the given element using TreeWalker. + /// + public static AutomationElement GetParentElement(AutomationElement element) + { + var walker = TreeWalker.ControlViewWalker; + var parent = walker.GetParent(element); + Assert.IsNotNull(parent, "Parent element was not found"); + + Console.WriteLine($"Parent element: {parent.Current.Name}"); + Console.WriteLine($"Parent Control Type: {parent.Current.ControlType.LocalizedControlType}"); + + return parent; + } + + /// + /// Finds an element by name and invokes it. Fails the test if element is not found or not invokable. + /// + public static void FindAndInvokeElement(AutomationElement parent, string elementName, int waitMs = 1000) + { + Condition condition = new PropertyCondition(AutomationElement.NameProperty, elementName); + var element = parent.FindFirst(TreeScope.Subtree, condition); + Assert.IsNotNull(element, $"Element with name '{elementName}' was not found"); + + Console.WriteLine($"Found element: {element.Current.Name}"); + Console.WriteLine($"Control Type: {element.Current.ControlType.LocalizedControlType}"); + + if (element.TryGetCurrentPattern(InvokePattern.Pattern, out object pattern)) + { + ((InvokePattern)pattern).Invoke(); + System.Threading.Thread.Sleep(waitMs); + } + else + { + Assert.Fail($"Invoke pattern not supported on '{elementName}' element"); + } + } + + /// + /// Finds an element by name and expands it. Fails the test if element is not found or doesn't support ExpandCollapse. + /// + public static void FindAndExpandElement(AutomationElement parent, string elementName, int waitMs = 1000) + { + Condition condition = new PropertyCondition(AutomationElement.NameProperty, elementName); + var element = parent.FindFirst(TreeScope.Subtree, condition); + Assert.IsNotNull(element, $"Element with name '{elementName}' was not found"); + + Console.WriteLine($"Found element: {element.Current.Name}"); + Console.WriteLine($"Control Type: {element.Current.ControlType.LocalizedControlType}"); + + if (element.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out object pattern)) + { + var expandCollapse = (ExpandCollapsePattern)pattern; + Console.WriteLine($"Current state: {expandCollapse.Current.ExpandCollapseState}"); + + if (expandCollapse.Current.ExpandCollapseState == ExpandCollapseState.Collapsed) + { + Console.WriteLine($"Expanding '{elementName}'..."); + expandCollapse.Expand(); + System.Threading.Thread.Sleep(waitMs); + Console.WriteLine($"New state: {expandCollapse.Current.ExpandCollapseState}"); + } + else + { + Console.WriteLine($"Element '{elementName}' is already expanded (state: {expandCollapse.Current.ExpandCollapseState})"); + } + } + else + { + Assert.Fail($"ExpandCollapse pattern not supported on '{elementName}' element"); + } + } + + /// + /// Finds an element by name and collapses it. Fails the test if element is not found or doesn't support ExpandCollapse. + /// + public static void FindAndCollapseElement(AutomationElement parent, string elementName, int waitMs = 1000) + { + Condition condition = new PropertyCondition(AutomationElement.NameProperty, elementName); + var element = parent.FindFirst(TreeScope.Subtree, condition); + Assert.IsNotNull(element, $"Element with name '{elementName}' was not found"); + + Console.WriteLine($"Found element: {element.Current.Name}"); + Console.WriteLine($"Control Type: {element.Current.ControlType.LocalizedControlType}"); + + if (element.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out object pattern)) + { + var expandCollapse = (ExpandCollapsePattern)pattern; + Console.WriteLine($"Current state: {expandCollapse.Current.ExpandCollapseState}"); + + if (expandCollapse.Current.ExpandCollapseState == ExpandCollapseState.Expanded) + { + Console.WriteLine($"Collapsing '{elementName}'..."); + expandCollapse.Collapse(); + System.Threading.Thread.Sleep(waitMs); + Console.WriteLine($"New state: {expandCollapse.Current.ExpandCollapseState}"); + } + else + { + Console.WriteLine($"Element '{elementName}' is already collapsed (state: {expandCollapse.Current.ExpandCollapseState})"); + } + } + else + { + Assert.Fail($"ExpandCollapse pattern not supported on '{elementName}' element"); + } + } + + /// + /// Common navigation: opens nav menu, clicks "All samples", then navigates to the specified component. + /// + public static void NavigateToComponent(AutomationElement galleryWindow, string componentName) + { + // Step 1: Open Navigation menu + FindAndInvokeElement(galleryWindow, "Navigation menu", waitMs: 2000); + + // Step 2: Click "All samples" + FindAndInvokeElement(galleryWindow, "All samples", waitMs: 2000); + + // Step 3: Navigate to the specific component + FindAndInvokeElement(galleryWindow, componentName); + } + } +} diff --git a/windows/AccessibilityUnitTest/Properties/AssemblyInfo.cs b/windows/AccessibilityUnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a2016f4a --- /dev/null +++ b/windows/AccessibilityUnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("AccessibilityUnitTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AccessibilityUnitTest")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("4494014e-461b-45a6-bac5-91fcd1936fdc")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/windows/AccessibilityUnitTest/app.config b/windows/AccessibilityUnitTest/app.config new file mode 100644 index 00000000..6309d356 --- /dev/null +++ b/windows/AccessibilityUnitTest/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/windows/AccessibilityUnitTest/packages.config b/windows/AccessibilityUnitTest/packages.config new file mode 100644 index 00000000..4811be12 --- /dev/null +++ b/windows/AccessibilityUnitTest/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/windows/rngallery.sln b/windows/rngallery.sln index 71ae15bc..504eb06a 100644 --- a/windows/rngallery.sln +++ b/windows/rngallery.sln @@ -9,64 +9,104 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rngallery", "rngallery\rnga EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Clipboard", "..\node_modules\@react-native-clipboard\clipboard\windows\Clipboard\Clipboard.vcxproj", "{90BFF18B-474B-445D-9847-B065853288D8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AccessibilityUnitTest", "AccessibilityUnitTest\AccessibilityUnitTest.csproj", "{4494014E-461B-45A6-BAC5-91FCD1936FDC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Debug|ARM64 = Debug|ARM64 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 Release|ARM64 = Release|ARM64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|Any CPU.ActiveCfg = Debug|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|Any CPU.Build.0 = Debug|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|Any CPU.Deploy.0 = Debug|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.Build.0 = Debug|ARM64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.Deploy.0 = Debug|ARM64 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x64.ActiveCfg = Debug|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x64.Build.0 = Debug|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x64.Deploy.0 = Debug|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x86.ActiveCfg = Debug|x86 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x86.Build.0 = Debug|x86 {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|x86.Deploy.0 = Debug|x86 - {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.Build.0 = Debug|ARM64 - {FD025CDF-36B7-4894-84AA-23772D926565}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|Any CPU.ActiveCfg = Release|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|Any CPU.Build.0 = Release|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|Any CPU.Deploy.0 = Release|x64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.ActiveCfg = Release|ARM64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.Build.0 = Release|ARM64 + {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.Deploy.0 = Release|ARM64 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x64.ActiveCfg = Release|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x64.Build.0 = Release|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x64.Deploy.0 = Release|x64 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x86.ActiveCfg = Release|x86 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x86.Build.0 = Release|x86 {FD025CDF-36B7-4894-84AA-23772D926565}.Release|x86.Deploy.0 = Release|x86 - {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.ActiveCfg = Release|ARM64 - {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.Build.0 = Release|ARM64 - {FD025CDF-36B7-4894-84AA-23772D926565}.Release|ARM64.Deploy.0 = Release|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|Any CPU.ActiveCfg = Debug|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|Any CPU.Build.0 = Debug|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|Any CPU.Deploy.0 = Debug|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.Build.0 = Debug|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.Deploy.0 = Debug|ARM64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x64.ActiveCfg = Debug|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x64.Build.0 = Debug|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x64.Deploy.0 = Debug|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x86.ActiveCfg = Debug|Win32 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x86.Build.0 = Debug|Win32 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|x86.Deploy.0 = Debug|Win32 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.Build.0 = Debug|ARM64 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|Any CPU.ActiveCfg = Release|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|Any CPU.Build.0 = Release|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|Any CPU.Deploy.0 = Release|x64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.ActiveCfg = Release|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.Build.0 = Release|ARM64 + {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.Deploy.0 = Release|ARM64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x64.ActiveCfg = Release|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x64.Build.0 = Release|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x64.Deploy.0 = Release|x64 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x86.ActiveCfg = Release|Win32 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x86.Build.0 = Release|Win32 {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|x86.Deploy.0 = Release|Win32 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.ActiveCfg = Release|ARM64 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.Build.0 = Release|ARM64 - {F84DF7D5-4C93-4DEE-BC66-74ADBA073950}.Release|ARM64.Deploy.0 = Release|ARM64 + {90BFF18B-474B-445D-9847-B065853288D8}.Debug|Any CPU.ActiveCfg = Debug|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Debug|Any CPU.Build.0 = Debug|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Debug|Any CPU.Deploy.0 = Debug|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.Build.0 = Debug|ARM64 {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x64.ActiveCfg = Debug|x64 {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x64.Build.0 = Debug|x64 {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x86.ActiveCfg = Debug|Win32 {90BFF18B-474B-445D-9847-B065853288D8}.Debug|x86.Build.0 = Debug|Win32 - {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {90BFF18B-474B-445D-9847-B065853288D8}.Debug|ARM64.Build.0 = Debug|ARM64 + {90BFF18B-474B-445D-9847-B065853288D8}.Release|Any CPU.ActiveCfg = Release|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Release|Any CPU.Build.0 = Release|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Release|Any CPU.Deploy.0 = Release|x64 + {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.ActiveCfg = Release|ARM64 + {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.Build.0 = Release|ARM64 {90BFF18B-474B-445D-9847-B065853288D8}.Release|x64.ActiveCfg = Release|x64 {90BFF18B-474B-445D-9847-B065853288D8}.Release|x64.Build.0 = Release|x64 {90BFF18B-474B-445D-9847-B065853288D8}.Release|x86.ActiveCfg = Release|Win32 {90BFF18B-474B-445D-9847-B065853288D8}.Release|x86.Build.0 = Release|Win32 {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.ActiveCfg = Release|ARM64 {90BFF18B-474B-445D-9847-B065853288D8}.Release|ARM64.Build.0 = Release|ARM64 + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|ARM64.Build.0 = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|x64.ActiveCfg = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|x64.Build.0 = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|x86.ActiveCfg = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Debug|x86.Build.0 = Debug|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|Any CPU.Build.0 = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|ARM64.ActiveCfg = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|ARM64.Build.0 = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|x64.ActiveCfg = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|x64.Build.0 = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|x86.ActiveCfg = Release|Any CPU + {4494014E-461B-45A6-BAC5-91FCD1936FDC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 3bf05cb107057c4c97a613b841f5fc1d3cb8b950 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:56:17 +0530 Subject: [PATCH 3/9] add unittest to pipelines --- azure-pipelines.yml | 51 +++++++++++++++++++++++++++++++++++++++++ ci.yml | 55 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index becc03c5..14284764 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,6 +55,57 @@ steps: inputs: script: yarn test + - task: CmdLine@2 + displayName: Build AccessibilityUnitTest + inputs: + script: msbuild windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj /p:Configuration=Debug /p:Platform=AnyCPU /restore + + - task: CmdLine@2 + displayName: Deploy App + timeoutInMinutes: 5 + inputs: + script: npx react-native run-windows --arch x64 --no-build --no-launch --release + + - task: PowerShell@2 + displayName: Launch App from Start Menu + inputs: + targetType: inline + script: | + Start-Process "shell:AppsFolder\Microsoft.ReactNativeGallery_8wekyb3d8bbwe!App" + Start-Sleep -Seconds 10 + + $process = Get-Process -Name "rngallery" -ErrorAction SilentlyContinue + if ($process) { + Write-Host "App is running (PID: $($process.Id))" + } else { + Write-Error "App failed to start" + } + + - task: VSTest@2 + displayName: Run Accessibility Tests + inputs: + testSelector: testAssemblies + testAssemblyVer2: | + **\AccessibilityUnitTest.dll + !**\obj\** + !**\ref\** + searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest + resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults + + - task: PublishBuildArtifacts@1 + displayName: Upload Accessibility Scan Results + condition: succeededOrFailed() + inputs: + pathtoPublish: windows\AccessibilityUnitTest\ScanResults + artifactName: 'Accessibility Scan Results - $(Agent.JobName)-$(System.JobAttempt)' + + - task: PublishBuildArtifacts@1 + displayName: Upload Accessibility Snapshot + condition: succeededOrFailed() + inputs: + pathtoPublish: windows\AccessibilityUnitTest\AccessibilitySnapshot.json + artifactName: 'Accessibility Snapshot - $(Agent.JobName)-$(System.JobAttempt)' + - task: PublishBuildArtifacts@1 displayName: Upload build logs condition: succeededOrFailed() diff --git a/ci.yml b/ci.yml index 9a4f621c..e1054aad 100644 --- a/ci.yml +++ b/ci.yml @@ -100,6 +100,61 @@ steps: inputs: script: del /f .\windows\rngallery\rngallery_Key.pfx + - task: CmdLine@2 + displayName: Build AccessibilityUnitTest + condition: eq(variables['PUBLISH_APP'], 'true') + inputs: + script: msbuild windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj /p:Configuration=Release /p:Platform=AnyCPU /restore + + - task: CmdLine@2 + displayName: Deploy App + condition: eq(variables['PUBLISH_APP'], 'true') + timeoutInMinutes: 5 + inputs: + script: npx react-native run-windows --arch x64 --no-build --no-launch --release + + - task: PowerShell@2 + displayName: Launch App from Start Menu + condition: eq(variables['PUBLISH_APP'], 'true') + inputs: + targetType: inline + script: | + Start-Process "shell:AppsFolder\Microsoft.ReactNativeGallery_8wekyb3d8bbwe!App" + Start-Sleep -Seconds 10 + + $process = Get-Process -Name "rngallery" -ErrorAction SilentlyContinue + if ($process) { + Write-Host "App is running (PID: $($process.Id))" + } else { + Write-Error "App failed to start" + } + + - task: VSTest@2 + displayName: Run Accessibility Tests + condition: eq(variables['PUBLISH_APP'], 'true') + inputs: + testSelector: testAssemblies + testAssemblyVer2: | + **\AccessibilityUnitTest.dll + !**\obj\** + !**\ref\** + searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest + resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults + + - task: PublishBuildArtifacts@1 + displayName: Upload Accessibility Scan Results + condition: and(succeededOrFailed(), eq(variables['PUBLISH_APP'], 'true')) + inputs: + pathtoPublish: windows\AccessibilityUnitTest\ScanResults + artifactName: 'Accessibility Scan Results - $(Agent.JobName)-$(System.JobAttempt)' + + - task: PublishBuildArtifacts@1 + displayName: Upload Accessibility Snapshot + condition: and(succeededOrFailed(), eq(variables['PUBLISH_APP'], 'true')) + inputs: + pathtoPublish: windows\AccessibilityUnitTest\AccessibilitySnapshot.json + artifactName: 'Accessibility Snapshot - $(Agent.JobName)-$(System.JobAttempt)' + - task: PublishBuildArtifacts@1 displayName: Upload App condition: and(succeededOrFailed(), eq(${{ matrix.reactNativeWindowsVersion }}, "current")) From d2c49ceb00ee3542e25103689633ad2424a40ec4 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:14:15 +0530 Subject: [PATCH 4/9] update to use msbuild --- azure-pipelines.yml | 7 +++++-- ci.yml | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 14284764..5783af4d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,10 +55,13 @@ steps: inputs: script: yarn test - - task: CmdLine@2 + - task: MSBuild@1 displayName: Build AccessibilityUnitTest inputs: - script: msbuild windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj /p:Configuration=Debug /p:Platform=AnyCPU /restore + solution: windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj + configuration: Debug + platform: AnyCPU + msbuildArguments: /restore - task: CmdLine@2 displayName: Deploy App diff --git a/ci.yml b/ci.yml index e1054aad..482dae75 100644 --- a/ci.yml +++ b/ci.yml @@ -100,11 +100,14 @@ steps: inputs: script: del /f .\windows\rngallery\rngallery_Key.pfx - - task: CmdLine@2 + - task: MSBuild@1 displayName: Build AccessibilityUnitTest condition: eq(variables['PUBLISH_APP'], 'true') inputs: - script: msbuild windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj /p:Configuration=Release /p:Platform=AnyCPU /restore + solution: windows\AccessibilityUnitTest\AccessibilityUnitTest.csproj + configuration: Release + platform: AnyCPU + msbuildArguments: /restore - task: CmdLine@2 displayName: Deploy App From ed0e1b7fd6f0aab9a283bae809c6394f08447c6d Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:30:28 +0530 Subject: [PATCH 5/9] Deploy in release mode --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5783af4d..7f3554b6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,7 +67,7 @@ steps: displayName: Deploy App timeoutInMinutes: 5 inputs: - script: npx react-native run-windows --arch x64 --no-build --no-launch --release + script: npx react-native run-windows --arch x64 --no-build --no-launch - task: PowerShell@2 displayName: Launch App from Start Menu From 207874b5b89b7235848ef5459d8594202d66ebf1 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:01:56 +0530 Subject: [PATCH 6/9] add release mode --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7f3554b6..1771b836 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,10 +45,10 @@ steps: script: yarn tsc | yarn lint - task: CmdLine@2 - displayName: Build project (Debug) + displayName: Build project (Release) timeoutInMinutes: 60 inputs: - script: npx react-native run-windows --arch x64 --no-deploy --logging --buildLogDirectory $BuildLogDirectory\Debug + script: npx react-native run-windows --arch x64 --no-deploy --logging --release --buildLogDirectory $BuildLogDirectory\Debug - task: CmdLine@2 displayName: Snapshot Tests @@ -67,7 +67,7 @@ steps: displayName: Deploy App timeoutInMinutes: 5 inputs: - script: npx react-native run-windows --arch x64 --no-build --no-launch + script: npx react-native run-windows --arch x64 --no-build --no-launch --release - task: PowerShell@2 displayName: Launch App from Start Menu From 50d05bc729b48850a738511bd58156b549dedf92 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:32:44 +0530 Subject: [PATCH 7/9] Reorder ZNavigation --- azure-pipelines.yml | 4 +- ci.yml | 4 +- .../AccessibilityTests.cs | 42 +++++++++---------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1771b836..911f5574 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -89,9 +89,7 @@ steps: inputs: testSelector: testAssemblies testAssemblyVer2: | - **\AccessibilityUnitTest.dll - !**\obj\** - !**\ref\** + **\bin\Debug\AccessibilityUnitTest.dll searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults diff --git a/ci.yml b/ci.yml index 482dae75..fb6cd2e0 100644 --- a/ci.yml +++ b/ci.yml @@ -138,9 +138,7 @@ steps: inputs: testSelector: testAssemblies testAssemblyVer2: | - **\AccessibilityUnitTest.dll - !**\obj\** - !**\ref\** + **\bin\Release\AccessibilityUnitTest.dll searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults diff --git a/windows/AccessibilityUnitTest/AccessibilityTests.cs b/windows/AccessibilityUnitTest/AccessibilityTests.cs index 7bf4a3fb..040d07d8 100644 --- a/windows/AccessibilityUnitTest/AccessibilityTests.cs +++ b/windows/AccessibilityUnitTest/AccessibilityTests.cs @@ -22,27 +22,6 @@ private IScanner GetTestScanner() return CreateScannerForTest(processId, TestContext.TestName); } - [TestMethod] - public void TestZNavigation() - { - // Navigate home first to reset UI state when running after other tests - FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); - FindAndInvokeElement(GalleryWindow, "Home", waitMs: 1000); - FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); - var navMenu = FindElement(GalleryWindow, "Navigation menu"); - var parent = GetParentElement(navMenu); - FindAndExpandElement(GalleryWindow, "Basic Input"); - FindAndExpandElement(GalleryWindow, "Collections"); - FindAndExpandElement(GalleryWindow, "Dialogs & flyouts"); - FindAndExpandElement(GalleryWindow, "Layout"); - FindAndExpandElement(GalleryWindow, "Media"); - FindAndExpandElement(GalleryWindow, "Scrolling"); - FindAndExpandElement(GalleryWindow, "Text"); - FindAndExpandElement(GalleryWindow, "System"); - FindAndExpandElement(GalleryWindow, "Legacy"); - AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner(), parent)); - } - [TestMethod] public void TestNavigateToHomeAndValidate() { @@ -164,6 +143,27 @@ public void TestNavigateToTouchableWithoutFeedbackAndValidate() AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner())); } + [TestMethod] + public void TestZNavigation() + { + // Navigate home first to reset UI state when running after other tests + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + FindAndInvokeElement(GalleryWindow, "Home", waitMs: 1000); + FindAndInvokeElement(GalleryWindow, "Navigation menu", waitMs: 2000); + var navMenu = FindElement(GalleryWindow, "Navigation menu"); + var parent = GetParentElement(navMenu); + FindAndExpandElement(GalleryWindow, "Basic Input"); + FindAndExpandElement(GalleryWindow, "Collections"); + FindAndExpandElement(GalleryWindow, "Dialogs & flyouts"); + FindAndExpandElement(GalleryWindow, "Layout"); + FindAndExpandElement(GalleryWindow, "Media"); + FindAndExpandElement(GalleryWindow, "Scrolling"); + FindAndExpandElement(GalleryWindow, "Text"); + FindAndExpandElement(GalleryWindow, "System"); + FindAndExpandElement(GalleryWindow, "Legacy"); + AddTestSnapshot(TestContext.TestName, GetScanResults(GetTestScanner(), parent)); + } + [ClassInitialize] public static void ClassInitialize(TestContext context) { From 4843650347ec986203d914276e53a184160c38c6 Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:13:35 +0530 Subject: [PATCH 8/9] Add Snapshot validation --- azure-pipelines.yml | 44 ++++++++++++++++++ ci.yml | 45 +++++++++++++++++++ .../AccessibilityUnitTest/GallerySession.cs | 4 +- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 911f5574..9fcc75f8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -84,6 +84,14 @@ steps: Write-Error "App failed to start" } + - task: PowerShell@2 + displayName: Save Baseline Accessibility Snapshot + inputs: + targetType: inline + script: | + Copy-Item "windows\AccessibilityUnitTest\AccessibilitySnapshot.json" "$(Build.ArtifactStagingDirectory)\AccessibilitySnapshot.baseline.json" + Write-Host "Baseline snapshot saved" + - task: VSTest@2 displayName: Run Accessibility Tests inputs: @@ -93,6 +101,42 @@ steps: searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults + - task: PowerShell@2 + displayName: Validate Accessibility Snapshot + condition: succeededOrFailed() + inputs: + targetType: inline + script: | + $baselinePath = "$(Build.ArtifactStagingDirectory)\AccessibilitySnapshot.baseline.json" + $currentPath = "windows\AccessibilityUnitTest\AccessibilitySnapshot.json" + + if (-not (Test-Path $currentPath)) { + Write-Host "##[error]Accessibility snapshot was not generated by the test run." + exit 1 + } + + # Sort results by TestName for stable comparison + function Normalize-Snapshot($path) { + $json = Get-Content $path -Raw | ConvertFrom-Json + $json.Results = @($json.Results | Sort-Object TestName) + return ($json | ConvertTo-Json -Depth 10) + } + + $baselineNorm = Normalize-Snapshot $baselinePath + $currentNorm = Normalize-Snapshot $currentPath + + if ($baselineNorm -eq $currentNorm) { + Write-Host "Accessibility snapshot matches baseline." + } else { + Write-Host "##[error]Accessibility snapshot does not match the baseline in the repo." + Write-Host "##[section]Baseline:" + Write-Host $baselineNorm + Write-Host "##[section]Current:" + Write-Host $currentNorm + Write-Host "##[error]Update AccessibilitySnapshot.json if the changes are intentional." + exit 1 + } + - task: PublishBuildArtifacts@1 displayName: Upload Accessibility Scan Results condition: succeededOrFailed() diff --git a/ci.yml b/ci.yml index fb6cd2e0..893c2353 100644 --- a/ci.yml +++ b/ci.yml @@ -132,6 +132,15 @@ steps: Write-Error "App failed to start" } + - task: PowerShell@2 + displayName: Save Baseline Accessibility Snapshot + condition: eq(variables['PUBLISH_APP'], 'true') + inputs: + targetType: inline + script: | + Copy-Item "windows\AccessibilityUnitTest\AccessibilitySnapshot.json" "$(Build.ArtifactStagingDirectory)\AccessibilitySnapshot.baseline.json" + Write-Host "Baseline snapshot saved" + - task: VSTest@2 displayName: Run Accessibility Tests condition: eq(variables['PUBLISH_APP'], 'true') @@ -142,6 +151,42 @@ steps: searchFolder: $(System.DefaultWorkingDirectory)\windows\AccessibilityUnitTest resultsFolder: $(Build.ArtifactStagingDirectory)\TestResults + - task: PowerShell@2 + displayName: Validate Accessibility Snapshot + condition: and(succeededOrFailed(), eq(variables['PUBLISH_APP'], 'true')) + inputs: + targetType: inline + script: | + $baselinePath = "$(Build.ArtifactStagingDirectory)\AccessibilitySnapshot.baseline.json" + $currentPath = "windows\AccessibilityUnitTest\AccessibilitySnapshot.json" + + if (-not (Test-Path $currentPath)) { + Write-Host "##[error]Accessibility snapshot was not generated by the test run." + exit 1 + } + + # Sort results by TestName for stable comparison + function Normalize-Snapshot($path) { + $json = Get-Content $path -Raw | ConvertFrom-Json + $json.Results = @($json.Results | Sort-Object TestName) + return ($json | ConvertTo-Json -Depth 10) + } + + $baselineNorm = Normalize-Snapshot $baselinePath + $currentNorm = Normalize-Snapshot $currentPath + + if ($baselineNorm -eq $currentNorm) { + Write-Host "Accessibility snapshot matches baseline." + } else { + Write-Host "##[error]Accessibility snapshot does not match the baseline in the repo." + Write-Host "##[section]Baseline:" + Write-Host $baselineNorm + Write-Host "##[section]Current:" + Write-Host $currentNorm + Write-Host "##[error]Update AccessibilitySnapshot.json if the changes are intentional." + exit 1 + } + - task: PublishBuildArtifacts@1 displayName: Upload Accessibility Scan Results condition: and(succeededOrFailed(), eq(variables['PUBLISH_APP'], 'true')) diff --git a/windows/AccessibilityUnitTest/GallerySession.cs b/windows/AccessibilityUnitTest/GallerySession.cs index 08895689..25471f62 100644 --- a/windows/AccessibilityUnitTest/GallerySession.cs +++ b/windows/AccessibilityUnitTest/GallerySession.cs @@ -98,7 +98,9 @@ public static void AddTestSnapshot(string testName, ScanOutput scanResults) Description = error.Rule.Description, HowToFix = error.Rule.HowToFix, Element = error.Element?.Properties != null - ? string.Join(", ", error.Element.Properties.Select(p => $"{p.Key}: {p.Value}")) + ? string.Join(", ", error.Element.Properties + .Where(p => p.Key == "ControlType" || p.Key == "LocalizedControlType" || p.Key == "Name" || p.Key == "HelpText" || p.Key == "IsEnabled" || p.Key == "IsOffscreen") + .Select(p => $"{p.Key}: {p.Value}")) : null }); } From 54ac0175a0e85aa3acc0f4af8fc62324566036ba Mon Sep 17 00:00:00 2001 From: vineethkuttan <66076509+vineethkuttan@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:46:49 +0530 Subject: [PATCH 9/9] update snapshot --- .../AccessibilitySnapshot.json | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/windows/AccessibilityUnitTest/AccessibilitySnapshot.json b/windows/AccessibilityUnitTest/AccessibilitySnapshot.json index ca95c678..77ea1557 100644 --- a/windows/AccessibilityUnitTest/AccessibilitySnapshot.json +++ b/windows/AccessibilityUnitTest/AccessibilitySnapshot.json @@ -3,167 +3,150 @@ "TotalErrors": 16, "Results": [ { - "TestName": "TestNavigateToClipboardAndValidate", - "ErrorCount": 1, + "TestName": "TestZNavigation", + "ErrorCount": 2, "Errors": [ { - "RuleId": 2, - "Description": "An on-screen element must not have a null BoundingRectangle property.", - "HowToFix": "If the element is off-screen, set its IsOffscreen property to true.\r\nIf the element is on-screen, provide a BoundingRectangle property.", - "Element": "RuntimeId: [2A,40CF8,4,88CE], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Button, IsEnabled: True, IsOffscreen: False" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Button, IsEnabled: True, IsOffscreen: False" } ] }, { - "TestName": "TestNavigateToTouchableWithoutFeedbackAndValidate", + "TestName": "TestNavigateToTouchableOpacityAndValidate", "ErrorCount": 2, "Errors": [ { "RuleId": 11, "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", - "Element": "RuntimeId: [2A,40CF8,4,D128], BoundingRectangle: [l=-1322,t=1279,r=-1282,b=1329], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Decreases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=40,h=50]" + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, IsEnabled: True, HelpText: Decreases the counter by 1, IsOffscreen: False" }, { "RuleId": 11, "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", - "Element": "RuntimeId: [2A,40CF8,4,D130], BoundingRectangle: [l=-1212,t=1279,r=-1172,b=1329], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Increases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=40,h=50]" + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, IsEnabled: True, HelpText: Increases the counter by 1, IsOffscreen: False" } ] }, { - "TestName": "TestNavigateToTextAndValidate", + "TestName": "TestNavigateToSwitchAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestNavigateToButtonAndValidate", - "ErrorCount": 6, - "Errors": [ - { - "RuleId": 123, - "Description": "The Name property must not include the element's control type.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", - "Element": "RuntimeId: [2A,40CF8,4,848A], BoundingRectangle: [l=-1300,t=226,r=-1195,b=264], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, HasKeyboardFocus: True, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=105,h=38]" - }, - { - "RuleId": 124, - "Description": "The Name must not include the same text as the LocalizedControlType.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", - "Element": "RuntimeId: [2A,40CF8,4,848A], BoundingRectangle: [l=-1300,t=226,r=-1195,b=264], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, HasKeyboardFocus: True, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=105,h=38]" - }, - { - "RuleId": 123, - "Description": "The Name property must not include the element's control type.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", - "Element": "RuntimeId: [2A,40CF8,4,84C4], BoundingRectangle: [l=-1304,t=413,r=-1191,b=450], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: colored button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=113,h=37]" - }, - { - "RuleId": 124, - "Description": "The Name must not include the same text as the LocalizedControlType.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", - "Element": "RuntimeId: [2A,40CF8,4,84C4], BoundingRectangle: [l=-1304,t=413,r=-1191,b=450], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: colored button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=113,h=37]" - }, - { - "RuleId": 123, - "Description": "The Name property must not include the element's control type.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", - "Element": "RuntimeId: [2A,40CF8,4,84FE], BoundingRectangle: [l=-1306,t=600,r=-1189,b=637], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=117,h=37]" - }, - { - "RuleId": 124, - "Description": "The Name must not include the same text as the LocalizedControlType.", - "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", - "Element": "RuntimeId: [2A,40CF8,4,84FE], BoundingRectangle: [l=-1306,t=600,r=-1189,b=637], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=117,h=37]" - } - ] + "TestName": "TestNavigateToTouchableHighlightAndValidate", + "ErrorCount": 0, + "Errors": [] }, { - "TestName": "TestNavigateToScrollViewAndValidate", + "TestName": "TestNavigateToTextInputAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestNavigateToImageAndValidate", + "TestName": "TestNavigateToViewAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestNavigateToFlatListAndValidate", + "TestName": "TestNavigateToPressableAndValidate", + "ErrorCount": 1, + "Errors": [ + { + "RuleId": 11, + "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", + "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Pressable, IsEnabled: False, IsOffscreen: False" + } + ] + }, + { + "TestName": "TestNavigateToHomeAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestNavigateToTouchableOpacityAndValidate", + "TestName": "TestNavigateToTouchableWithoutFeedbackAndValidate", "ErrorCount": 2, "Errors": [ { "RuleId": 11, "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", - "Element": "RuntimeId: [2A,40CF8,4,C9DC], BoundingRectangle: [l=-1342,t=1452,r=-1292,b=1492], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Decreases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=50,h=40]" + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Decrease counter. Current value is 0, IsEnabled: True, HelpText: Decreases the counter by 1, IsOffscreen: False" }, { "RuleId": 11, "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", - "Element": "RuntimeId: [2A,40CF8,4,C9E6], BoundingRectangle: [l=-1202,t=1452,r=-1152,b=1492], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, HelpText: Increases the counter by 1, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=50,h=40]" + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Increase counter. Current value is 0, IsEnabled: True, HelpText: Increases the counter by 1, IsOffscreen: False" } ] }, { - "TestName": "TestNavigateToSettingsAndValidate", + "TestName": "TestNavigateToFlatListAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestZNavigation", - "ErrorCount": 2, + "TestName": "TestNavigateToButtonAndValidate", + "ErrorCount": 6, "Errors": [ { "RuleId": 123, "Description": "The Name property must not include the element's control type.", "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", - "Element": "RuntimeId: [2A,40CF8,4,E118], BoundingRectangle: [l=-2544,t=-26,r=-2206,b=8], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=338,h=34]" + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, IsEnabled: True, IsOffscreen: False" }, { "RuleId": 124, "Description": "The Name must not include the same text as the LocalizedControlType.", "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", - "Element": "RuntimeId: [2A,40CF8,4,E118], BoundingRectangle: [l=-2544,t=-26,r=-2206,b=8], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Button, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=338,h=34]" - } - ] - }, - { - "TestName": "TestNavigateToViewAndValidate", - "ErrorCount": 0, - "Errors": [] - }, - { - "TestName": "TestNavigateToPressableAndValidate", - "ErrorCount": 1, - "Errors": [ + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Simple Button, IsEnabled: True, IsOffscreen: False" + }, { - "RuleId": 11, - "Description": "A button must support one of these patterns: Invoke, Toggle, or ExpandCollapse.", - "HowToFix": "Modify the button to support exactly one of the following patterns:\r\n · Support the Invoke pattern if the button performs a command at the request of the user.\r\n · Support the Toggle pattern if the button can cycle through a series of up to three states.\r\n · Support the ExpandCollapse pattern if the button shows or hides additional content.", - "Element": "RuntimeId: [2A,40CF8,4,A468], BoundingRectangle: [l=-1317,t=429,r=-1177,b=479], ProcessId: 26036, ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Pressable, HasKeyboardFocus: False, IsKeyboardFocusable: True, IsEnabled: False, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=140,h=50]" + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: colored button, IsEnabled: True, IsOffscreen: False" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: colored button, IsEnabled: True, IsOffscreen: False" + }, + { + "RuleId": 123, + "Description": "The Name property must not include the element's control type.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the control type.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, IsEnabled: False, IsOffscreen: False" + }, + { + "RuleId": 124, + "Description": "The Name must not include the same text as the LocalizedControlType.", + "HowToFix": "Provide a UI Automation Name property for the element that:\r\n · Concisely identifies the element, AND\r\n · Does not include the same text as the element's LocalizedControlType property.", + "Element": "ControlType: Button(50000), LocalizedControlType: button, Name: Disabled Button, IsEnabled: False, IsOffscreen: False" } ] }, { - "TestName": "TestNavigateToTouchableHighlightAndValidate", - "ErrorCount": 0, - "Errors": [] - }, - { - "TestName": "TestNavigateToTextInputAndValidate", + "TestName": "TestNavigateToSettingsAndValidate", "ErrorCount": 0, "Errors": [] }, { - "TestName": "TestNavigateToSwitchAndValidate", + "TestName": "TestNavigateToTextAndValidate", "ErrorCount": 0, "Errors": [] }, @@ -175,18 +158,35 @@ "RuleId": 2, "Description": "An on-screen element must not have a null BoundingRectangle property.", "HowToFix": "If the element is off-screen, set its IsOffscreen property to true.\r\nIf the element is on-screen, provide a BoundingRectangle property.", - "Element": "RuntimeId: [2A,40CF8,4,9E76], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, Name: , HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + "Element": "ControlType: Text(50020), LocalizedControlType: text, Name: , IsEnabled: True, IsOffscreen: False" }, { "RuleId": 133, "Description": "The Name property must not contain only whitespace.", "HowToFix": "Provide a UI Automation Name property that concisely identifies the element.", - "Element": "RuntimeId: [2A,40CF8,4,9E76], ProcessId: 26036, ControlType: Text(50020), LocalizedControlType: text, Name: , HasKeyboardFocus: False, IsKeyboardFocusable: False, IsEnabled: True, Culture: 0, IsControlElement: True, IsContentElement: True, IsPassword: False, NativeWindowHandle: 0, IsOffscreen: False, Orientation: None(0), IsRequiredForForm: False, IsDataValidForForm: False, ProviderDescription: [pid:26036,providerId:0x0 Main(parent link):Unidentified Provider (unmanaged:Microsoft.ReactNative.dll)], OptimizeForVisualContent: False, LiveSetting: 0, IsPeripheral: False, FillType: 0, VisualEffects: 0, HeadingLevel: HeadingLevel_None (80050), IsDialog: False, LogicalSize: [w=0,h=0]" + "Element": "ControlType: Text(50020), LocalizedControlType: text, Name: , IsEnabled: True, IsOffscreen: False" } ] }, { - "TestName": "TestNavigateToHomeAndValidate", + "TestName": "TestNavigateToClipboardAndValidate", + "ErrorCount": 1, + "Errors": [ + { + "RuleId": 2, + "Description": "An on-screen element must not have a null BoundingRectangle property.", + "HowToFix": "If the element is off-screen, set its IsOffscreen property to true.\r\nIf the element is on-screen, provide a BoundingRectangle property.", + "Element": "ControlType: Text(50020), LocalizedControlType: text, IsEnabled: True, IsOffscreen: False" + } + ] + }, + { + "TestName": "TestNavigateToScrollViewAndValidate", + "ErrorCount": 0, + "Errors": [] + }, + { + "TestName": "TestNavigateToImageAndValidate", "ErrorCount": 0, "Errors": [] }