| // 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_emoticon_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_symbol_item_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::PickerSearchResult::Emoji(u"😊", u"happy"), |
| ash::PickerSearchResult::Symbol(u"♬", u"music"), |
| ash::PickerSearchResult::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::PickerSearchResult::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::PickerSearchResult::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::PickerSearchResult::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::PickerSearchResult::Text(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 |