chromium/chrome/browser/ui/ash/picker/picker_accessibility_browsertest.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/constants/ash_features.h"
#include "ash/picker/metrics/picker_performance_metrics.h"
#include "ash/picker/model/picker_action_type.h"
#include "ash/picker/model/picker_search_results_section.h"
#include "ash/picker/picker_controller.h"
#include "ash/picker/views/mock_picker_search_results_view_delegate.h"
#include "ash/picker/views/picker_emoji_bar_view.h"
#include "ash/picker/views/picker_emoji_item_view.h"
#include "ash/picker/views/picker_feature_tour.h"
#include "ash/picker/views/picker_item_with_submenu_view.h"
#include "ash/picker/views/picker_key_event_handler.h"
#include "ash/picker/views/picker_list_item_view.h"
#include "ash/picker/views/picker_preview_bubble_controller.h"
#include "ash/picker/views/picker_pseudo_focus.h"
#include "ash/picker/views/picker_search_bar_textfield.h"
#include "ash/picker/views/picker_search_field_view.h"
#include "ash/picker/views/picker_search_results_view.h"
#include "ash/picker/views/picker_section_list_view.h"
#include "ash/picker/views/picker_section_view.h"
#include "ash/picker/views/picker_view_delegate.h"
#include "ash/public/cpp/picker/picker_search_result.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/icon_button.h"
#include "ash/test/test_widget_builder.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/accessibility/speech_monitor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/picker/picker_client_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/strings/grit/components_strings.h"
#include "components/user_manager/user_manager.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/browsertest_util.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"

namespace {

// These tests are not meant to be end-to-end tests.
// They should test Picker view components on a low-level.
// They are browser tests in order to bring in ChromeVox so that we can test
// announcements.
class PickerAccessibilityBrowserTest : public InProcessBrowserTest {
 public:
  PickerAccessibilityBrowserTest() {
    // This is needed to turn off Dogfood checks that prevents Picker from being
    // toggled.
    scoped_feature_list_.InitWithFeatureState(ash::features::kPickerDogfood,
                                              false);
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();

    ash::AccessibilityManager::Get()->EnableSpokenFeedback(true);
    // Ignore the intro.
    sm_.ExpectSpeechPattern("ChromeVox*");
    // Disable earcons which can be annoying in tests.
    sm_.Call([this]() {
      ImportJSModuleForChromeVox("ChromeVox",
                                 "/chromevox/background/chromevox.js");
      DisableEarcons();
    });

    ash::PickerController::DisableFeatureTourForTesting();
  }

  void TearDownOnMainThread() override {
    ash::AccessibilityManager::Get()->EnableSpokenFeedback(false);
    InProcessBrowserTest::TearDownOnMainThread();
  }

 protected:
  ash::test::SpeechMonitor sm_;

 private:
  void ImportJSModuleForChromeVox(std::string_view name,
                                  std::string_view path) {
    extensions::browsertest_util::ExecuteScriptInBackgroundPageDeprecated(
        ash::AccessibilityManager::Get()->profile(),
        extension_misc::kChromeVoxExtensionId,
        base::ReplaceStringPlaceholders(
            R"(import('$1').then(mod => {
            globalThis.$2 = mod.$2;
            window.domAutomationController.send('done');
          }))",
            {std::string(path), std::string(name)}, nullptr));
  }

  void DisableEarcons() {
    extensions::browsertest_util::ExecuteScriptInBackgroundPageNoWait(
        ash::AccessibilityManager::Get()->profile(),
        extension_misc::kChromeVoxExtensionId,
        "ChromeVox.earcons.playEarcon = function() {};");
  }

  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingEmptySearchFieldAnnouncesPlaceholder) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  view->SetPlaceholderText(u"cat");

  sm_.Call([view]() { view->RequestFocus(); });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       SetDescendantAnnouncesDescendant) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* container_view =
      widget->SetContentsView(views::Builder<views::BoxLayoutView>().Build());
  auto* search_field_view =
      container_view->AddChildView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  auto* other_view =
      container_view->AddChildView(std::make_unique<views::Label>(u"test"));
  search_field_view->SetPlaceholderText(u"cat");

  sm_.Call([search_field_view]() { search_field_view->RequestFocus(); });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");

  sm_.Call([search_field_view, other_view]() {
    search_field_view->SetTextfieldActiveDescendant(other_view);
  });

  sm_.ExpectSpeechPattern("test");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       SetDescendantAnnouncesDescendantAfterKeyEvent) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* container_view =
      widget->SetContentsView(views::Builder<views::BoxLayoutView>().Build());
  auto* search_field_view =
      container_view->AddChildView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  auto* other_view =
      container_view->AddChildView(std::make_unique<views::Label>(u"test"));
  search_field_view->SetPlaceholderText(u"cat");

  sm_.Call([search_field_view]() { search_field_view->RequestFocus(); });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");

  sm_.Call([search_field_view, other_view]() {
    ui::test::EventGenerator event_generator(
        ash::Shell::Get()->GetPrimaryRootWindow());
    event_generator.PressAndReleaseKey(ui::VKEY_A);
    search_field_view->SetTextfieldActiveDescendant(other_view);
  });

  sm_.ExpectSpeechPattern("A");
  sm_.ExpectSpeechPattern("test");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       SetDescendantToTextfieldAnnouncesPlaceholder) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* container_view =
      widget->SetContentsView(views::Builder<views::BoxLayoutView>().Build());
  auto* search_field_view =
      container_view->AddChildView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  auto* other_view =
      container_view->AddChildView(std::make_unique<views::Label>(u"test"));
  search_field_view->SetPlaceholderText(u"cat");

  sm_.Call([search_field_view]() { search_field_view->RequestFocus(); });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");

  sm_.Call([search_field_view, other_view]() {
    search_field_view->SetTextfieldActiveDescendant(other_view);
  });

  sm_.ExpectSpeechPattern("test");

  sm_.Call([search_field_view]() {
    search_field_view->SetTextfieldActiveDescendant(
        search_field_view->textfield());
  });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");
  sm_.Replay();
}

// TODO(crbug.com/356567533): flaky on MSAN. Deflake and re-enable the test.
#if defined(MEMORY_SANITIZER)
#define MAYBE_SetDescendantThenFocusingSearchFieldAnnouncesDescendant \
  DISABLED_SetDescendantThenFocusingSearchFieldAnnouncesDescendant
#else
#define MAYBE_SetDescendantThenFocusingSearchFieldAnnouncesDescendant \
  SetDescendantThenFocusingSearchFieldAnnouncesDescendant
#endif
IN_PROC_BROWSER_TEST_F(
    PickerAccessibilityBrowserTest,
    MAYBE_SetDescendantThenFocusingSearchFieldAnnouncesDescendant) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* container_view =
      widget->SetContentsView(views::Builder<views::BoxLayoutView>().Build());
  auto* search_field_view =
      container_view->AddChildView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  auto* other_view =
      container_view->AddChildView(std::make_unique<views::Label>(u"test"));
  search_field_view->SetPlaceholderText(u"cat");

  sm_.Call([search_field_view, other_view]() {
    search_field_view->SetTextfieldActiveDescendant(other_view);
    search_field_view->RequestFocus();
  });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");
  sm_.ExpectSpeechPattern("test");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingNonEmptySearchFieldAnnouncesPlaceholder) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  view->SetPlaceholderText(u"cat");
  view->SetQueryText(u"query");

  sm_.Call([view]() { view->RequestFocus(); });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Edit text");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingSearchFieldClearButtonAnnouncesTooltip) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  view->SetPlaceholderText(u"placeholder");

  sm_.Call([view]() {
    view->textfield_for_testing().InsertOrReplaceText(u"a");
    view->clear_button_for_testing().RequestFocus();
  });

  sm_.ExpectSpeechPattern(
      l10n_util::GetStringUTF8(IDS_APP_LIST_CLEAR_SEARCHBOX));
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingSearchFieldBackButtonAnnouncesTooltip) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::PickerKeyEventHandler key_event_handler;
  ash::PickerPerformanceMetrics metrics;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchFieldView>(
          base::DoNothing(), base::DoNothing(), &key_event_handler, &metrics));
  view->SetPlaceholderText(u"placeholder");
  view->SetBackButtonVisible(true);

  sm_.Call([view]() { view->back_button_for_testing().RequestFocus(); });

  sm_.ExpectSpeechPattern(l10n_util::GetStringUTF8(IDS_ACCNAME_BACK));
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingEmojiBarItemsAnnouncesGrid) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/1000,
          /*is_gifs_enabled=*/true));
  view->SetSearchResults({
      ash::PickerEmojiResult::Emoji(u"😊", u"happy"),
      ash::PickerEmojiResult::Symbol(u"♬", u"music"),
      ash::PickerEmojiResult::Emoticon(u"(°□°)", u"surprise"),
  });

  sm_.Call([view]() { view->GetItemsForTesting()[0]->RequestFocus(); });

  sm_.ExpectSpeechPattern("happy emoji");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("row 1 column 1");
  sm_.ExpectSpeechPattern("Table Emojis and GIFs, 1 by 5");

  sm_.Call([view]() { view->GetItemsForTesting()[1]->RequestFocus(); });

  sm_.ExpectSpeechPattern("music");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("row 1 column 2");

  sm_.Call([view]() { view->GetItemsForTesting()[2]->RequestFocus(); });

  sm_.ExpectSpeechPattern("surprise emoticon");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("row 1 column 3");

  sm_.Call([view]() { view->gifs_button_for_testing()->RequestFocus(); });

  sm_.ExpectSpeechPattern("GIF");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("row 1 column 4");

  sm_.Call(
      [view]() { view->more_emojis_button_for_testing()->RequestFocus(); });

  sm_.ExpectSpeechPattern(l10n_util::GetStringUTF8(
      IDS_PICKER_MORE_EMOJIS_AND_GIFS_BUTTON_ACCESSIBLE_NAME));
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("row 1 column 5");

  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingGifsButtonAnnouncesLabel) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/100,
          /*is_gifs_enabled=*/true));

  sm_.Call([view]() { view->gifs_button_for_testing()->RequestFocus(); });

  sm_.ExpectSpeechPattern("GIF");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingMoreEmojisAnnouncesTooltip) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/100));

  sm_.Call(
      [view]() { view->more_emojis_button_for_testing()->RequestFocus(); });

  sm_.ExpectSpeechPattern(
      l10n_util::GetStringUTF8(IDS_PICKER_MORE_EMOJIS_BUTTON_ACCESSIBLE_NAME));
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       SectionsAnnouncesHeadings) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSectionListView>(
          /*section_width=*/100, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr));
  view->AddSection()->AddTitleLabel(u"Section1");
  view->AddSection()->AddTitleLabel(u"Section2");
  ui::test::EventGenerator event_generator(
      ash::Shell::Get()->GetPrimaryRootWindow());

  sm_.Call([view, &event_generator]() {
    view->RequestFocus();
    event_generator.PressAndReleaseKeyAndModifierKeys(ui::KeyboardCode::VKEY_H,
                                                      ui::EF_COMMAND_DOWN);
  });

  sm_.ExpectSpeechPattern("Section1");
  sm_.ExpectSpeechPattern("Heading");

  sm_.Call([&event_generator]() {
    event_generator.PressAndReleaseKeyAndModifierKeys(ui::KeyboardCode::VKEY_H,
                                                      ui::EF_COMMAND_DOWN);
  });

  sm_.ExpectSpeechPattern("Section2");
  sm_.ExpectSpeechPattern("Heading");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingSectionShowAllAnnounces) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSectionListView>(
          /*section_width=*/100, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr));
  ash::PickerSectionView* section_view = view->AddSection();
  section_view->AddTitleLabel(u"Section");
  section_view->AddTitleTrailingLink(u"Show all", u"cat", base::DoNothing());

  sm_.Call([section_view]() {
    section_view->title_trailing_link_for_testing()->RequestFocus();
  });

  sm_.ExpectSpeechPattern("cat");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       ListItemAnnouncesTextWithAction) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSectionListView>(
          /*section_width=*/100, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr));
  ash::PickerSectionView* section = view->AddSection();
  section->AddTitleLabel(u"Section1");
  ash::PickerListItemView* item = section->AddListItem(
      std::make_unique<ash::PickerListItemView>(base::DoNothing()));
  item->SetLeadingIcon(ui::ImageModel::FromImage(gfx::test::CreateImage(1)));
  item->SetPrimaryText(u"primary");
  item->SetSecondaryText(u"secondary");
  item->SetBadgeAction(ash::PickerActionType::kInsert);
  item->SetBadgeVisible(true);

  sm_.Call([item]() { item->RequestFocus(); });

  sm_.ExpectSpeechPattern("Insert primary, secondary");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       ListItemAnnouncesPreviewMetadata) {
  ash::PickerPreviewBubbleController preview_controller;
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSectionListView>(
          /*section_width=*/100, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr));
  ash::PickerSectionView* section = view->AddSection();
  section->AddTitleLabel(u"Section1");
  ash::PickerListItemView* item = section->AddListItem(
      std::make_unique<ash::PickerListItemView>(base::DoNothing()));
  item->SetPrimaryText(u"primary");
  base::test::TestFuture<void> file_info_future;
  base::File::Info file_info;
  EXPECT_TRUE(
      base::Time::FromString("23 Dec 2021 09:01:00", &file_info.last_modified));
  item->SetPreview(
      &preview_controller,
      file_info_future.GetSequenceBoundCallback().Then(
          base::ReturnValueOnce<std::optional<base::File::Info>>(file_info)),
      base::FilePath(), base::DoNothing(),
      /*update_icon=*/true);
  ASSERT_TRUE(file_info_future.Wait());

  sm_.Call([item]() { item->RequestFocus(); });

  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Edited · Dec 23");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingItemInSectionListViewAnnouncesSizeAndPosition) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSectionListView>(
          /*section_width=*/100, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr));
  ash::PickerSectionView* section1 = view->AddSection();
  ash::PickerListItemView* item1 = section1->AddListItem(
      std::make_unique<ash::PickerListItemView>(base::DoNothing()));
  item1->SetPrimaryText(u"item1");
  ash::PickerListItemView* item2 = section1->AddListItem(
      std::make_unique<ash::PickerListItemView>(base::DoNothing()));
  item2->SetPrimaryText(u"item2");
  ash::PickerSectionView* section2 = view->AddSection();
  ash::PickerListItemView* item3 = section2->AddListItem(
      std::make_unique<ash::PickerListItemView>(base::DoNothing()));
  item3->SetPrimaryText(u"item3");

  sm_.Call([item1]() { item1->RequestFocus(); });

  sm_.ExpectSpeechPattern("item1");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("List item");
  sm_.ExpectSpeechPattern("1 of 2");
  sm_.ExpectSpeechPattern("List");
  sm_.ExpectSpeechPattern("with 2 items");

  sm_.Call([item2]() { item2->RequestFocus(); });

  sm_.ExpectSpeechPattern("item2");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("List item");
  sm_.ExpectSpeechPattern("2 of 2");

  sm_.Call([item3]() { item3->RequestFocus(); });

  sm_.ExpectSpeechPattern("item3");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("List item");
  sm_.ExpectSpeechPattern("1 of 1");
  sm_.ExpectSpeechPattern("List");
  sm_.ExpectSpeechPattern("with 1 item");

  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingItemWithSubmenuAnnouncesMenuRole) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view = widget->SetContentsView(
      std::make_unique<ash::PickerItemWithSubmenuView>());
  view->SetLeadingIcon(ui::ImageModel::FromImage(gfx::test::CreateImage(1)));
  view->SetText(u"meow");

  sm_.Call([view]() { view->RequestFocus(); });

  sm_.ExpectSpeechPattern("meow");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("has pop up");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingEmojiResultButtonAnnouncesNameOfEmoji) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/1000));
  view->SetSearchResults({ash::PickerEmojiResult::Emoji(u"😊", u"happy")});

  sm_.Call([view]() { view->GetItemsForTesting().front()->RequestFocus(); });

  sm_.ExpectSpeechPattern("happy emoji");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingSymbolResultButtonAnnouncesNameOfSymbol) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/1000));
  view->SetSearchResults({ash::PickerEmojiResult::Symbol(u"♬", u"music")});

  sm_.Call([view]() { view->GetItemsForTesting().front()->RequestFocus(); });

  sm_.ExpectSpeechPattern("music");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       FocusingEmoticonResultButtonAnnouncesNameOfEmoticon) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerEmojiBarView>(
          /*delegate=*/nullptr, /*picker_width=*/1000));
  view->SetSearchResults(
      {ash::PickerEmojiResult::Emoticon(u"(°□°)", u"surprise")});

  sm_.Call([view]() { view->GetItemsForTesting().front()->RequestFocus(); });

  sm_.ExpectSpeechPattern("surprise emoticon");
  sm_.ExpectSpeechPattern("Button");
  sm_.ExpectSpeechPattern("Press * to activate");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       StoppingSearchAnnouncesEmojiResults) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::MockPickerSearchResultsViewDelegate mock_delegate;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchResultsView>(
          &mock_delegate, /*picker_width=*/1000, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr, /*preview_controller=*/nullptr));

  sm_.Call([view]() {
    view->SetNumEmojiResultsForA11y(5);
    view->SearchStopped(/*illustration=*/{}, /*description=*/u"");
  });
  sm_.ExpectSpeechPattern("5 emojis. No other results.");
  sm_.ExpectSpeechPattern("Status");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       StoppingSearchAnnouncesNoResults) {
  std::unique_ptr<views::Widget> widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  ash::MockPickerSearchResultsViewDelegate mock_delegate;
  auto* view =
      widget->SetContentsView(std::make_unique<ash::PickerSearchResultsView>(
          &mock_delegate, /*picker_width=*/1000, /*asset_fetcher=*/nullptr,
          /*submenu_controller=*/nullptr, /*preview_controller=*/nullptr));

  sm_.Call([view]() {
    view->SearchStopped(/*illustration=*/{}, /*description=*/u"");
  });
  sm_.ExpectSpeechPattern(l10n_util::GetStringUTF8(IDS_PICKER_NO_RESULTS_TEXT));
  sm_.ExpectSpeechPattern("Status");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       ShowingFeatureTourAnnouncesContents) {
  ash::PickerFeatureTour feature_tour;

  sm_.Call([this, &feature_tour]() {
    feature_tour.MaybeShowForFirstUse(
        browser()->profile()->GetPrefs(),
        ash::PickerFeatureTour::EditorStatus::kEligible, base::DoNothing(),
        base::DoNothing());
  });

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  sm_.ExpectSpeechPattern("*insert content*");
#endif
  sm_.ExpectSpeechPattern("Dialog");
  sm_.ExpectSpeechPattern("Get started");
  sm_.ExpectSpeechPattern("Button");
  sm_.Replay();
}

IN_PROC_BROWSER_TEST_F(PickerAccessibilityBrowserTest,
                       InsertingAnnouncesInsertionBeforeTextfieldRefocus) {
  ash::PickerController controller;
  controller.DisableFeatureKeyCheck();
  PickerClientImpl client(&controller, user_manager::UserManager::Get());
  std::unique_ptr<views::Widget> textfield_widget =
      ash::TestWidgetBuilder()
          .SetWidgetType(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS)
          .BuildClientOwnsWidget();
  auto* textfield =
      textfield_widget->SetContentsView(std::make_unique<views::Textfield>());
  textfield->GetViewAccessibility().SetName(u"textfield");
  sm_.Call([&textfield]() { textfield->RequestFocus(); });
  sm_.ExpectSpeechPattern("textfield");
  sm_.ExpectSpeechPattern("Edit text");

  sm_.Call([&controller]() {
    controller.ToggleWidget();
    controller.CloseWidgetThenInsertResultOnNextFocus(
        ash::PickerTextResult(u"abc"));
  });

  sm_.ExpectSpeechPattern("Picker");
  sm_.ExpectSpeechPattern(", window");
  sm_.ExpectSpeechPattern("Picker");
  sm_.ExpectSpeechPattern("Status");
  sm_.ExpectSpeechPattern("Inserting selected result");
  sm_.ExpectSpeechPattern("textfield");
  sm_.ExpectSpeechPattern("abc");
  sm_.ExpectSpeechPattern("Edit text");
  sm_.Replay();
}

}  // namespace