// 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 <memory>
#include <vector>
#include "ash/ash_element_identifiers.h"
#include "ash/picker/picker_controller.h"
#include "ash/picker/views/picker_emoji_item_view.h"
#include "ash/picker/views/picker_list_item_view.h"
#include "ash/shell.h"
#include "base/files/file_util.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time_override.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/test/base/ash/interactive/interactive_ash_test.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/test/history_service_test_util.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/browser/browsertest_util.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/state_observer.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/view_observer.h"
namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWebInputFieldFocusedEvent);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kWebInputFieldValueEvent);
class ViewFocusObserver
: public ui::test::
ObservationStateObserver<bool, views::View, views::ViewObserver> {
public:
explicit ViewFocusObserver(views::View* view)
: ObservationStateObserver(view) {}
~ViewFocusObserver() override = default;
// ui::test::ObservationStateObserver:
bool GetStateObserverInitialState() const override {
return source()->HasFocus();
}
// views::ViewObserver:
void OnViewFocused(views::View* observed_view) override {
if (observed_view == source()) {
OnStateObserverStateChanged(true);
}
}
void OnViewBlurred(views::View* observed_view) override {
if (observed_view == source()) {
OnStateObserverStateChanged(false);
}
}
void OnViewIsDeleting(views::View* observed_view) override {
OnObservationStateObserverSourceDestroyed();
}
};
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ViewFocusObserver,
kSearchFieldFocusedState);
void TogglePickerByAccelerator() {
ui_controls::SendKeyPress(/*window=*/nullptr, ui::VKEY_F,
/*control=*/false, /*shift=*/false,
/*alt=*/false, /*command=*/true);
}
void SendKeyPressWithoutModifiers(ui::KeyboardCode key) {
ui_controls::SendKeyPress(/*window=*/nullptr, key,
/*control=*/false, /*shift=*/false,
/*alt=*/false, /*command=*/false);
}
void AddUrlToHistory(Profile* profile,
GURL url,
base::Time last_visit = base::Time::Now()) {
history::HistoryService* history_service =
HistoryServiceFactory::GetForProfile(profile,
ServiceAccessType::EXPLICIT_ACCESS);
history_service->AddPageWithDetails(url, /*title=*/u"", /*visit_count=*/1,
/*typed_count=*/1,
/*last_visit=*/last_visit,
/*hidden=*/false,
history::SOURCE_BROWSED);
history::BlockUntilHistoryProcessesPendingRequests(history_service);
}
bool AddLocalFileToDownloads(Profile* profile, const std::string& file_name) {
const base::FilePath mount_path =
file_manager::util::GetDownloadsFolderForProfile(profile);
base::FilePath absolute_path = mount_path.AppendASCII(file_name);
{
base::ScopedAllowBlockingForTesting allow_blocking;
return base::WriteFile(absolute_path, file_name);
}
}
class PickerInteractiveUiTest : public InteractiveAshTest {
public:
const WebContentsInteractionTestUtil::DeepQuery kInputFieldQuery{
"input[type=\"text\"]",
};
PickerInteractiveUiTest() {
ash::PickerController::DisableFeatureKeyCheck();
ash::PickerController::DisableFeatureTourForTesting();
}
void SetUpOnMainThread() override {
InteractiveAshTest::SetUpOnMainThread();
// Set up context for element tracking for InteractiveAshTest.
SetupContextWidget();
}
auto WaitForWebInputFieldFocus() {
StateChange expected_state;
expected_state.type = StateChange::Type::kExistsAndConditionTrue;
expected_state.where = kInputFieldQuery;
expected_state.test_function = "el => el === document.activeElement";
expected_state.event = kWebInputFieldFocusedEvent;
return Steps(WaitForStateChange(kWebContentsElementId, expected_state));
}
auto WaitForWebInputFieldValue(std::u16string_view value) {
StateChange expected_state;
expected_state.type = StateChange::Type::kExistsAndConditionTrue;
expected_state.where = kInputFieldQuery;
expected_state.test_function =
content::JsReplace("el => el.value === $1", value);
expected_state.event = kWebInputFieldValueEvent;
return Steps(WaitForStateChange(kWebContentsElementId, expected_state));
}
// Same as `NameDescendantView` but matches on a property of the view.
template <typename V, typename R, typename T>
auto NameDescendantViewByProperty(ElementSpecifier view,
std::string_view name,
R (V::*property)() const,
T&& value) {
return NameDescendantView(
view, name,
base::BindLambdaForTesting([property, value](const views::View* view) {
if (const auto* v = views::AsViewClass<V>(view)) {
return (v->*property)() == value;
}
return false;
}));
}
private:
base::test::ScopedFeatureList feature_list_{ash::features::kPicker};
};
// Searches for 'thumbs up', checks the top emoji result is '👍', and inserts it
// into a web input field.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchAndInsertEmoji) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kFirstEmojiResultName = "FirstEmojiResult";
constexpr std::u16string_view kExpectedFirstEmoji = u"👍";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"thumbs up"),
WaitForShow(ash::kPickerEmojiItemElementId,
/*transition_only_on_event=*/true),
NameDescendantViewByProperty(
ash::kPickerEmojiBarElementId, kFirstEmojiResultName,
&ash::PickerEmojiItemView::GetTextForTesting, kExpectedFirstEmoji),
PressButton(kFirstEmojiResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context,
WaitForWebInputFieldValue(kExpectedFirstEmoji)));
}
// Searches for 'greek letter alpha', checks the top emoji result is 'α'; and
// inserts it into a web input field.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchAndInsertSymbol) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kFirstSymbolResultName = "FirstSymbolResult";
constexpr std::u16string_view kExpectedFirstSymbol = u"α";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId,
u"greek letter alpha"),
WaitForShow(ash::kPickerEmojiItemElementId,
/*transition_only_on_event=*/true),
NameDescendantViewByProperty(
ash::kPickerEmojiBarElementId, kFirstSymbolResultName,
&ash::PickerEmojiItemView::GetTextForTesting, kExpectedFirstSymbol),
PressButton(kFirstSymbolResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context,
WaitForWebInputFieldValue(kExpectedFirstSymbol)));
}
// Searches for 'denko of disapproval', checks the top emoji result is 'ಠωಠ';
// and inserts it into a web input field.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchAndInsertEmoticon) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kFirstEmoticonResultName = "FirstEmoticonResult";
constexpr std::u16string_view kExpectedFirstEmoticon = u"ಠωಠ";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId,
u"denko of disapproval"),
WaitForShow(ash::kPickerEmojiItemElementId,
/*transition_only_on_event=*/true),
NameDescendantViewByProperty(
ash::kPickerEmojiBarElementId, kFirstEmoticonResultName,
&ash::PickerEmojiItemView::GetTextForTesting, kExpectedFirstEmoticon),
PressButton(kFirstEmoticonResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context,
WaitForWebInputFieldValue(kExpectedFirstEmoticon)));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchAndSelectMoreEmojis) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"thumbs"),
WaitForShow(ash::kPickerMoreEmojisElementId),
PressButton(ash::kPickerMoreEmojisElementId),
WaitForHide(ash::kPickerElementId),
WaitForShow(ash::kEmojiPickerElementId));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchGifs) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"happy"),
WaitForShow(ash::kPickerGifElementId),
PressButton(ash::kPickerGifElementId), WaitForHide(ash::kPickerElementId),
WaitForShow(ash::kEmojiPickerElementId));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchBrowsingHistory) {
AddUrlToHistory(GetActiveUserProfile(), GURL("https://foo.com/history"));
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kHistoryResultName = "HistoryResult";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"foo.com"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kHistoryResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting,
u"foo.com/history"),
PressButton(kHistoryResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context,
WaitForWebInputFieldValue(u"https://foo.com/history")));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchBrowsingHistoryCategory) {
AddUrlToHistory(GetActiveUserProfile(), GURL("https://foo.com/history"));
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kHistoryCategoryResultName =
"HistoryCategoryResult";
constexpr std::string_view kHistoryResultName = "HistoryResult";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"history"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kHistoryCategoryResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting,
u"Browsing history"),
PressButton(kHistoryCategoryResultName),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"f"),
WaitForShow(ash::kPickerSearchResultsPageElementId,
/*transition_only_on_event=*/true),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kHistoryResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting,
u"foo.com/history"),
PressButton(kHistoryResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context,
WaitForWebInputFieldValue(u"https://foo.com/history")));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchLocalFile) {
ASSERT_TRUE(AddLocalFileToDownloads(GetActiveUserProfile(), "test.png"));
// TODO: b/360229206 - Use a contenteditable input field so the file can be
// inserted.
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kFileResultName = "FileResult";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"test"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kFileResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting, u"test.png"),
PressButton(kFileResultName), WaitForHide(ash::kPickerElementId));
}
// Searches for 'today', checks the top result is the date, and inserts it
// into a web input field.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, SearchAndInsertDate) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kDateResultName = "DateResult";
constexpr std::u16string_view kExpectedDate = u"Feb 19";
views::Textfield* picker_search_field = nullptr;
base::subtle::ScopedTimeClockOverrides time_override(
[]() {
base::Time date;
bool result = base::Time::FromString("19 Feb 2024 12:00 GMT", &date);
CHECK(result);
return date;
},
/*time_ticks_override=*/nullptr,
/*thread_ticks_override=*/nullptr);
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"today"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kDateResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting, kExpectedDate),
PressButton(kDateResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context, WaitForWebInputFieldValue(kExpectedDate)));
}
// Searches for '1 + 1', checks the top result is '2', and inserts it
// into a web input field.
// TODO: crbug.com/355618977 - Fix flakiness.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, DISABLED_SearchAndInsertMath) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kMathResultName = "MathResult";
constexpr std::u16string_view kExpectedResult = u"2";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"1 + 1"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kMathResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting, kExpectedResult),
PressButton(kMathResultName), WaitForHide(ash::kPickerElementId),
InContext(browser_context, WaitForWebInputFieldValue(kExpectedResult)));
}
// Navigates through the zero-state UI using only the keyboard.
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, KeyboardNavigationInZeroState) {
ASSERT_TRUE(CreateBrowserWindow(
GURL("data:text/html,<input type=\"text\" autofocus/>")));
const ui::ElementContext browser_context =
chrome::FindLastActive()->window()->GetElementContext();
constexpr std::string_view kItem1Name = "Item1";
constexpr std::string_view kItem2Name = "Item2";
constexpr std::string_view kEmoji1Name = "Emoji1";
constexpr std::string_view kEmoji2Name = "Emoji2";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
InContext(browser_context, Steps(InstrumentTab(kWebContentsElementId),
WaitForWebInputFieldFocus())),
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
NameDescendantViewByType<ash::PickerListItemView>(ash::kPickerElementId,
kItem1Name, 0),
NameDescendantViewByType<ash::PickerListItemView>(ash::kPickerElementId,
kItem2Name, 1),
NameDescendantViewByType<ash::PickerEmojiItemView>(ash::kPickerElementId,
kEmoji1Name, 0),
NameDescendantViewByType<ash::PickerEmojiItemView>(ash::kPickerElementId,
kEmoji2Name, 1),
// The first item should be selected by default.
CheckViewProperty(kItem1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
// Down arrow should move to the next item.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_DOWN); }),
CheckViewProperty(kItem2Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kItem1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal),
// Up arrow should move to the previous item.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_UP); }),
CheckViewProperty(kItem1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kItem2Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal),
// Up arrow should move to the first emoji in the emoji bar.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_UP); }),
CheckViewProperty(kEmoji1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kItem1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal),
// Right arrow should move to the second emoji in the emoji bar.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_RIGHT); }),
CheckViewProperty(kEmoji2Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kEmoji1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal),
// Left arrow should back move to the first emoji in the emoji bar.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_LEFT); }),
CheckViewProperty(kEmoji1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kEmoji2Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal),
// Down arrow should back move to the first item.
Do([]() { SendKeyPressWithoutModifiers(ui::VKEY_DOWN); }),
CheckViewProperty(kItem1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kPseudoFocused),
CheckViewProperty(kEmoji1Name, &ash::PickerItemView::GetItemState,
ash::PickerItemView::ItemState::kNormal));
}
IN_PROC_BROWSER_TEST_F(PickerInteractiveUiTest, LocalFilePreview) {
ASSERT_TRUE(AddLocalFileToDownloads(GetActiveUserProfile(), "test.png"));
constexpr std::string_view kFileResultName = "FileResult";
views::Textfield* picker_search_field = nullptr;
RunTestSequence(
Do([]() { TogglePickerByAccelerator(); }),
AfterShow(ash::kPickerSearchFieldTextfieldElementId,
[&picker_search_field](ui::TrackedElement* el) {
picker_search_field = AsView<views::Textfield>(el);
}),
ObserveState(kSearchFieldFocusedState, std::ref(picker_search_field)),
WaitForState(kSearchFieldFocusedState, true),
EnterText(ash::kPickerSearchFieldTextfieldElementId, u"test"),
WaitForShow(ash::kPickerSearchResultsPageElementId),
WaitForShow(ash::kPickerSearchResultsListItemElementId),
NameDescendantViewByProperty(
ash::kPickerSearchResultsPageElementId, kFileResultName,
&ash::PickerListItemView::GetPrimaryTextForTesting, u"test.png"),
MoveMouseTo(kFileResultName),
WaitForShow(ash::kPickerPreviewBubbleElementId));
}
} // namespace