[a11y][annotation] Adds node descriptor utterances to screen reader.

This change adds descriptors for nodes of various roles to screen reader
output.

TEST: fx test a11y_lib_tests
Change-Id: I7e500bc3615d520fed59cbdc7287aedd37b8d11f
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/402966
Testability-Review: Alexander Brusher <abrusher@google.com>
Commit-Queue: Alexander Brusher <abrusher@google.com>
Reviewed-by: Lucas Radaelli <lucasradaelli@google.com>
diff --git a/src/ui/a11y/lib/screen_reader/node_describer.cc b/src/ui/a11y/lib/screen_reader/node_describer.cc
index 1b77a37..8ac63db 100644
--- a/src/ui/a11y/lib/screen_reader/node_describer.cc
+++ b/src/ui/a11y/lib/screen_reader/node_describer.cc
@@ -27,6 +27,26 @@
   return utterance;
 }
 
+// Returns a message that describes a node where Role == HEADER.
+NodeDescriber::UtteranceAndContext DescribeHeader(a11y::i18n::MessageFormatter* formatter) {
+  NodeDescriber::UtteranceAndContext utterance;
+  auto message = formatter->FormatStringById(static_cast<uint64_t>(MessageIds::ROLE_HEADER));
+  FX_DCHECK(message);
+  utterance.utterance.set_message(std::move(*message));
+  utterance.delay = kDefaultDelay;
+  return utterance;
+}
+
+// Returns a message that describes a node where Role == IMAGE.
+NodeDescriber::UtteranceAndContext DescribeImage(a11y::i18n::MessageFormatter* formatter) {
+  NodeDescriber::UtteranceAndContext utterance;
+  auto message = formatter->FormatStringById(static_cast<uint64_t>(MessageIds::ROLE_IMAGE));
+  FX_DCHECK(message);
+  utterance.utterance.set_message(std::move(*message));
+  utterance.delay = kDefaultDelay;
+  return utterance;
+}
+
 }  // namespace
 
 NodeDescriber::NodeDescriber(std::unique_ptr<i18n::MessageFormatter> message_formatter)
@@ -51,6 +71,10 @@
     if (node->has_role()) {
       if (node->role() == Role::BUTTON) {
         description.emplace_back(DescribeButton(message_formatter_.get()));
+      } else if (node->role() == Role::HEADER) {
+        description.emplace_back(DescribeHeader(message_formatter_.get()));
+      } else if (node->role() == Role::IMAGE) {
+        description.emplace_back(DescribeImage(message_formatter_.get()));
       }
     }
   }
diff --git a/src/ui/a11y/lib/screen_reader/tests/node_describer_unittest.cc b/src/ui/a11y/lib/screen_reader/tests/node_describer_unittest.cc
index 44dd2c5..b8ed773 100644
--- a/src/ui/a11y/lib/screen_reader/tests/node_describer_unittest.cc
+++ b/src/ui/a11y/lib/screen_reader/tests/node_describer_unittest.cc
@@ -64,5 +64,33 @@
   ASSERT_EQ(result[1].utterance.message(), "button");
 }
 
+TEST_F(NodeDescriberTest, NodeHeader) {
+  Node node;
+  node.mutable_attributes()->set_label("foo");
+  node.set_role(Role::HEADER);
+  mock_message_formatter_ptr_->SetMessageForId(static_cast<uint64_t>(MessageIds::ROLE_HEADER),
+                                               "header");
+  auto result = node_describer_->DescribeNode(&node);
+  ASSERT_EQ(result.size(), 2u);
+  ASSERT_TRUE(result[0].utterance.has_message());
+  ASSERT_EQ(result[0].utterance.message(), "foo");
+  ASSERT_TRUE(result[1].utterance.has_message());
+  ASSERT_EQ(result[1].utterance.message(), "header");
+}
+
+TEST_F(NodeDescriberTest, NodeImage) {
+  Node node;
+  node.mutable_attributes()->set_label("foo");
+  node.set_role(Role::IMAGE);
+  mock_message_formatter_ptr_->SetMessageForId(static_cast<uint64_t>(MessageIds::ROLE_IMAGE),
+                                               "image");
+  auto result = node_describer_->DescribeNode(&node);
+  ASSERT_EQ(result.size(), 2u);
+  ASSERT_TRUE(result[0].utterance.has_message());
+  ASSERT_EQ(result[0].utterance.message(), "foo");
+  ASSERT_TRUE(result[1].utterance.has_message());
+  ASSERT_EQ(result[1].utterance.message(), "image");
+}
+
 }  // namespace
 }  // namespace accessibility_test