Skip to content
Merged
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
30 changes: 16 additions & 14 deletions source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,20 @@ def getControlFieldSpeech( # noqa: C901
description: Optional[str] = None
_descriptionFrom = attrs.get('_description-from', controlTypes.DescriptionFrom.UNKNOWN)
_descriptionIsContent: bool = attrs.get("descriptionIsContent", False)
_reportDescriptionAsAnnotation: bool = (
# Don't report other sources of description like "title" all the time
# The usages of these is not consistent and often does not seem to have
# Screen Reader users in mind
config.conf["annotations"]["reportAriaDescription"]
and not _descriptionIsContent
and controlTypes.DescriptionFrom.ARIA_DESCRIPTION == _descriptionFrom
and reason in (
OutputReason.FOCUS,
OutputReason.QUICKNAV,
OutputReason.CARET,
OutputReason.SAYALL,
)
)
if (
(
config.conf["presentation"]["reportObjectDescriptions"]
Expand All @@ -1795,19 +1809,7 @@ def getControlFieldSpeech( # noqa: C901
# Not used internally, but may be used by addons.
attrs.get('alwaysReportDescription', False)
)
or (
# Don't report other sources of description like "title" all the time
# The usages of these is not consistent and often does not seem to have
# Screen Reader users in mind
config.conf["annotations"]["reportAriaDescription"]
and not _descriptionIsContent
and controlTypes.DescriptionFrom.ARIA_DESCRIPTION == _descriptionFrom
and reason in (
OutputReason.FOCUS,
OutputReason.CARET,
OutputReason.SAYALL,
)
)
or _reportDescriptionAsAnnotation
):
description = attrs.get('description')

Expand Down Expand Up @@ -2024,7 +2026,7 @@ def getControlFieldSpeech( # noqa: C901
out = []
if isCurrent != controlTypes.IsCurrent.NO:
out.extend(isCurrentSequence)
if descriptionSequence:
if descriptionSequence and _reportDescriptionAsAnnotation:
out.extend(descriptionSequence)
# Speak expanded / collapsed / level for treeview items (in ARIA treegrids)
if role == controlTypes.Role.TREEVIEWITEM:
Expand Down
196 changes: 196 additions & 0 deletions tests/system/robot/chromeTests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1133,3 +1133,199 @@ def test_ensureNoBrowseModeDescription():
]),
message="Test focus mode with reportObjectDescriptions=False"
)


def test_quickNavTargetReporting():
"""
When using quickNav, the target object should be spoken first, inner context should be given before outer
context.
"""
spy = _NvdaLib.getSpyLib()
REPORT_ARTICLES = ["documentFormatting", "reportArticles"]
spy.set_configValue(REPORT_ARTICLES, False)

_chrome.prepareChrome(
"""
<div
aria-describedby="descId"
aria-labelledby="labelId"
role="article"
>
<h1>Quick Nav Target</h1>
<div id="labelId">
<div>Some name.</div>
</div>
<div id="descId">
<span>A bunch of text.</span>
</div>
</div>
"""
)
# Quick nav to heading
actualSpeech = _chrome.getSpeechAfterKey("h")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"Quick Nav Target", # Heading content (quick nav target), should read first
"heading", # Heading role
"level 1", # Heading level
])
)
# Reset to allow trying again with report articles enabled
actualSpeech = _chrome.getSpeechAfterKey("control+home")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"Before Test Case Marker",
])
)

# Quick nav to heading with report articles enabled
spy.set_configValue(REPORT_ARTICLES, True)
actualSpeech = _chrome.getSpeechAfterKey("h")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"Quick Nav Target", # Heading content (quick nav target), should read first
"heading", # Heading role
"level 1", # Heading level
"article", # article role, enabled via report article
"A bunch of text.", # article (ancestor) description
])
)


def test_focusTargetReporting():
"""
When moving focus the target object should be spoken first, inner context should be given before outer
context.
"""
spy = _NvdaLib.getSpyLib()
REPORT_ARTICLES = ["documentFormatting", "reportArticles"]
spy.set_configValue(REPORT_ARTICLES, False)

_chrome.prepareChrome(
"""
<a href="#">before Target</a>
<div
aria-describedby="descId"
aria-labelledby="labelId"
role="article"
>
<a href="#">Focus Target</a>
<div id="labelId">
<div>Some name.</div>
</div>
<div id="descId">
<span>A bunch of text.</span>
</div>
</div>
"""
)
# Set focus
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"before Target",
"link",
])
)

# Focus the link
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"Focus Target", # link content (focus target), should read first
"link", # link role
]),
message="browse mode - focus with Report Articles disabled"
)
# Reset to allow trying again with report articles enabled
actualSpeech = _chrome.getSpeechAfterKey("shift+tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"before Target",
"link",
])
)

# Focus the link with report articles enabled
spy.set_configValue(REPORT_ARTICLES, True)
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"Focus Target", # link content (focus target), should read first
"link", # link role
"article", # article role, enabled via report article
"A bunch of text.", # article (ancestor) description
]),
message="browse mode - focus with Report Articles enabled"
)

# Reset to allow trying again in focus mode
actualSpeech = _chrome.getSpeechAfterKey("shift+tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"before Target",
"link",
])
)

# Force focus mode
actualSpeech = _chrome.getSpeechAfterKey("NVDA+space")
_asserts.strings_match(
actualSpeech,
"Focus mode"
)

spy.set_configValue(REPORT_ARTICLES, False)
# Focus the link
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
SPEECH_CALL_SEP.join([
SPEECH_SEP.join([
"Some name.", # name for article
"article", # article role, enabled via report article
"A bunch of text.", # description for article
]),
SPEECH_SEP.join([
"Focus Target", # link content (focus target), should read first
"link", # link role
]),
]),
message="focus mode - focus with Report Articles disabled"
)
# Reset to allow trying again with report articles enabled
actualSpeech = _chrome.getSpeechAfterKey("shift+tab")
_asserts.strings_match(
actualSpeech,
SPEECH_SEP.join([
"before Target",
"link",
])
)

# Focus the link with report articles enabled
spy.set_configValue(REPORT_ARTICLES, True)
actualSpeech = _chrome.getSpeechAfterKey("tab")
_asserts.strings_match(
actualSpeech,
SPEECH_CALL_SEP.join([
SPEECH_SEP.join([
"Some name.", # name for article
"article", # article role, enabled via report article
"A bunch of text.", # description for article
]),
SPEECH_SEP.join([
"Focus Target", # link content (focus target), should read first
"link", # link role
]),
]),
message="focus mode - focus with Report Articles enabled"
)
7 changes: 6 additions & 1 deletion tests/system/robot/chromeTests.robot
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,9 @@ Prevent Duplicate Speech From Description while in Browse mode with tab nav
Only report description in focus mode due to reportObjectDescriptions
[Documentation] The term object in reportObjectDescriptions (essentially) means focus mode.
test_ensureNoBrowseModeDescription

Quick Nav reports target first
[Documentation] Quick Nav target should always be reported before ancestors. Ancestors should be reported from inner to outer.
test_quickNavTargetReporting
Focus reports target first
[Documentation] Focus target should always be reported before ancestors. Ancestors should be reported from inner to outer.
test_focusTargetReporting