Repository: nvaccess/nvda

Issue:
NVDA not announcing aria-label on lists
Steps to reproduce:

1. Go to https://codepen.io/Venkatesh-a11y/pen/OxZWdv
2. As soon as page loads focus lands on edit field so hit ESC key to switch to browse mode.
3. Hit M to move to the result frame.
4. Now arrow down to both unordered and ordered list over there.
5. Or hit L to navigate lists on the page.

Expected behavior:

NVDA should announce the aria-label before announcing the list item when focus enters list with quick key L or with down arrow otherwise context will miss for the users though specific context is described for each and every list with aria-label.
In the mentioned example, when user hits L NVDA should announce "list with two items here are the list of fruits I like bullet apples" and in the next hit of L it should announce "list with two items here are the list of vegetables I like one potato".

Actual Result:

NVDA doesn't announce the aria-label with quick key L or while arrowing down the list instead it announces the number of list items followed by the first list item directly.
In the mentioned example, NVDA announces "list with two items bullet Apples" and "list with two items one Potato" when focus enters lists.

System configuration:

NVDA Version: 2017.3, Installed

Other information:

OS version: Windows 10
Browser Versions: Firefox 56, Chrome 61, IE 11, Microsoft Edge 41
Tested with NVDA 2017.1 and 2017.2 also and the issue repro's.



Discussion:
I can reproduce this, and I agree this does not seem like ideal behaviour. I have a feeling I have seen a similar issue raised previously, the best I could find right now are:
* Report the Title Attribute on HTML lists and Tables #2137
* Report label in browse mode if it isn't rendered anywhere else (e.g. aria-label) #4773

Is there a past version of NVDA where this used to work as expected?
I am still having this issue with NVDA version 2018.2.1
cc: @michaelDCurran

Resolving patch:
diff --git a/source/speech.py b/source/speech.py
--- a/source/speech.py
+++ b/source/speech.py
@@ -1166,12 +1166,16 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
 	# speakStatesFirst: Speak the states before the role.
 	speakStatesFirst=role==controlTypes.ROLE_LINK
 
+	containerContainsText="" #: used for item counts for lists
+
 	# Determine what text to speak.
 	# Special cases
-	if speakEntry and childControlCount and fieldType=="start_addedToControlFieldStack" and role==controlTypes.ROLE_LIST and controlTypes.STATE_READONLY in states:
+	if childControlCount and fieldType=="start_addedToControlFieldStack" and role==controlTypes.ROLE_LIST and controlTypes.STATE_READONLY in states:
 		# List.
-		# Translators: Speaks number of items in a list (example output: list with 5 items).
-		return roleText+" "+_("with %s items")%childControlCount
+		# #7652: containerContainsText variable is set here, but the actual generation of all other output is handled further down in the general cases section.
+		# This ensures that properties such as name, states and level etc still get reported appropriately.
+		# Translators: Number of items in a list (example output: list with 5 items).
+		containerContainsText=_("with %s items")%childControlCount
 	elif fieldType=="start_addedToControlFieldStack" and role==controlTypes.ROLE_TABLE and tableID:
 		# Table.
 		return " ".join((nameText,roleText,stateText, getSpeechTextForProperties(_tableID=tableID, rowCount=attrs.get("table-rowcount"), columnCount=attrs.get("table-columncount")),levelText))
@@ -1196,8 +1200,8 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
 			+ (" %s" % stateText if stateText else "")
 			+ (" %s" % ariaCurrentText if ariaCurrent else ""))
 
-	# General cases
-	elif (
+	# General cases.
+	if (
 		(speakEntry and ((speakContentFirst and fieldType in ("end_relative","end_inControlFieldStack")) or (not speakContentFirst and fieldType in ("start_addedToControlFieldStack","start_relative"))))
 		or (speakWithinForLine and not speakContentFirst and not extraDetail and fieldType=="start_inControlFieldStack")
 	):
@@ -1209,7 +1213,7 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
 			if valueText:
 				log.error("valueText exists when expected none: valueText:'%s' placeholderText:'%s'"%(valueText,placeholderText))
 			valueText = placeholderText
-		out.extend(x for x in (nameText,(stateText if speakStatesFirst else roleText),(roleText if speakStatesFirst else stateText),ariaCurrentText,valueText,descriptionText,levelText,keyboardShortcutText) if x)
+		out.extend(x for x in (nameText,(stateText if speakStatesFirst else roleText),(roleText if speakStatesFirst else stateText),containerContainsText,ariaCurrentText,valueText,descriptionText,levelText,keyboardShortcutText) if x)
 		if content and not speakContentFirst:
 			out.append(content)
 		return CHUNK_SEPARATOR.join(out)


Test patch: