Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions source/NVDAObjects/UIA/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2565,13 +2565,24 @@ def _get_positionInfo(self):


class MenuItem(UIA):
def _get_description(self):
def _get_description(self) -> str | None:
name = self.name
description = super(MenuItem, self)._get_description()
if description != name:
return description
else:
return None
description = super()._get_description()
if not description or description == name:
providerDescription = self.UIAElement.cachedProviderDescription or ""
if (
self.UIAElement.cachedFrameworkID == "WinForm"
and "System.Windows.Forms, Version=4.0.0.0" in providerDescription
and "ToolStripMenuItem" in providerDescription
):
legacyDescription = self._getUIACacheablePropertyValue_handlesCOMErrors(
UIAHandler.UIA_LegacyIAccessibleDescriptionPropertyId,
ignoreDefault=True,
onError=None,
)
if isinstance(legacyDescription, str):
description = legacyDescription
return description if description != name else None


class UIColumnHeader(UIA):
Expand Down
82 changes: 82 additions & 0 deletions tests/unit/test_NVDAObjects_UIA.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2026 NV Access Limited, Cary-rowen
# This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license.
# For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt

"""Unit tests for NVDAObjects.UIA."""

import unittest
from unittest.mock import Mock, patch

from NVDAObjects.UIA import MenuItem, UIA


class TestMenuItemDescription(unittest.TestCase):
def test_legacyDescriptionFallback(self) -> None:
menuItem = object.__new__(MenuItem)
menuItem.name = "Name"
menuItem.UIAElement = Mock(
cachedFrameworkID="WinForm",
cachedProviderDescription=(
"managed:System.Windows.Forms.ToolStripMenuItem+ToolStripMenuItemAccessibleObject, "
"System.Windows.Forms, Version=4.0.0.0"
),
)
notSupportedValue = object()
testCases = (
("UIA description", "Legacy description", "UIA description", False),
("", "Legacy description", "Legacy description", True),
("Name", "Legacy description", "Legacy description", True),
("Name", "Name", None, True),
("Name", notSupportedValue, None, True),
)
for uiaDescription, legacyDescription, expectedDescription, shouldReadLegacyDescription in testCases:
with (
self.subTest(
uiaDescription=uiaDescription,
legacyDescription=legacyDescription,
),
patch.object(UIA, "_get_description", return_value=uiaDescription),
patch.object(
MenuItem,
"_getUIACacheablePropertyValue_handlesCOMErrors",
return_value=legacyDescription,
) as getLegacyDescription,
):
self.assertEqual(expectedDescription, menuItem._get_description())
if shouldReadLegacyDescription:
getLegacyDescription.assert_called_once()
else:
getLegacyDescription.assert_not_called()

def test_legacyDescriptionFallbackIsLimitedToNetFrameworkWinFormsToolStripMenuItems(self) -> None:
menuItem = object.__new__(MenuItem)
menuItem.name = "Name"
for frameworkID, providerDescription in (
(
"WPF",
"managed:System.Windows.Forms.ToolStripMenuItem+ToolStripMenuItemAccessibleObject, "
"System.Windows.Forms, Version=4.0.0.0",
),
(
"WinForm",
"managed:System.Windows.Forms.ToolStripMenuItem+ToolStripMenuItemAccessibleObject, "
"System.Windows.Forms, Version=8.0.0.0",
),
("WinForm", "System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0"),
):
with (
self.subTest(frameworkID=frameworkID, providerDescription=providerDescription),
patch.object(UIA, "_get_description", return_value=""),
patch.object(
MenuItem,
"_getUIACacheablePropertyValue_handlesCOMErrors",
return_value="Legacy description",
) as getLegacyDescription,
):
menuItem.UIAElement = Mock(
cachedFrameworkID=frameworkID,
cachedProviderDescription=providerDescription,
)
self.assertEqual("", menuItem._get_description())
getLegacyDescription.assert_not_called()
1 change: 1 addition & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
Previously these keys had no function when pressed on their own. (#20366, @fla-rion)
* The HID keyboard input simulation setting for ALVA braille displays is now remembered across reconnects and restarts. (#20455, @Cary-rowen)
* Braille now follows the spoken text during say all in browse mode when braille is tethered to focus. (#3287, @LeonarddeR)
* Object descriptions are now reported for .NET Framework Windows Forms ToolStrip menu items exposed through UI Automation. (#20486, @Cary-rowen)

### Changes for Developers

Expand Down
Loading