// Copyright 2022 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/system/unified/feature_tile.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/accessibility/ax_enums.mojom-shared.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/gfx/geometry/rrect_f.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/animation/ink_drop_state.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_class_properties.h"
namespace ash {
constexpr int kDefaultButtonRadius = 16;
namespace {
class MockFeaturePodController : public FeaturePodControllerBase {
public:
MockFeaturePodController() = default;
explicit MockFeaturePodController(bool togglable) : togglable_(togglable) {}
MockFeaturePodController(const MockFeaturePodController&) = delete;
MockFeaturePodController& operator=(const MockFeaturePodController&) = delete;
~MockFeaturePodController() override = default;
std::unique_ptr<FeatureTile> CreateTile(bool compact = false) override {
auto tile = std::make_unique<FeatureTile>(
base::BindRepeating(&FeaturePodControllerBase::OnLabelPressed,
weak_ptr_factory_.GetWeakPtr()),
togglable_,
compact ? FeatureTile::TileType::kCompact
: FeatureTile::TileType::kPrimary);
tile->SetVectorIcon(vector_icons::kDogfoodIcon);
tile->SetIconClickCallback(
base::BindRepeating(&MockFeaturePodController::OnIconPressed,
weak_ptr_factory_.GetWeakPtr()));
tile_ = tile.get();
return tile;
}
QsFeatureCatalogName GetCatalogName() override {
return QsFeatureCatalogName::kUnknown;
}
void OnIconPressed() override {
was_icon_pressed_ = true;
// FeaturePodController elements in production know if they are togglable,
// but in this mock we need to check before changing the toggled state. This
// attempts to match UX specs: tiles with clickable icons should toggle when
// their icon is clicked rather than their label.
if (togglable_ && tile_->is_icon_clickable()) {
toggled_ = !toggled_;
tile_->SetToggled(toggled_);
}
}
void OnLabelPressed() override {
was_label_pressed_ = true;
// FeaturePodController elements in production know if they are togglable,
// but in this mock we need to check before changing the toggled state. This
// attempts to match UX specs: tiles with clickable icons should toggle when
// their icon is clicked rather than their label.
if (togglable_ && !tile_->is_icon_clickable()) {
toggled_ = !toggled_;
tile_->SetToggled(toggled_);
}
}
bool WasIconPressed() { return was_icon_pressed_; }
bool WasLabelPressed() { return was_label_pressed_; }
private:
raw_ptr<FeatureTile> tile_ = nullptr;
bool was_icon_pressed_ = false;
bool was_label_pressed_ = false;
bool togglable_ = false;
bool toggled_ = false;
base::WeakPtrFactory<MockFeaturePodController> weak_ptr_factory_{this};
};
} // namespace
class FeatureTileTest
: public AshTestBase,
public testing::WithParamInterface</*IsVcDlcUiEnabled*/ bool> {
public:
FeatureTileTest() = default;
FeatureTileTest(const FeatureTileTest&) = delete;
FeatureTileTest& operator=(const FeatureTileTest&) = delete;
~FeatureTileTest() override = default;
// AshTestBase:
void SetUp() override {
if (IsVcDlcUiEnabled()) {
scoped_feature_list_
.InitWithFeatures(/*enabled_features=*/
{features::kFeatureManagementVideoConference,
features::kVcDlcUi},
/*disabled_features=*/{});
// Need to create a fake VC tray controller if VcDlcUi is enabled because
// this implies `features::IsVideoConferenceEnabled()` is true, and when
// that is true the VC tray is created (and the VC tray depends on the
// VC tray controller being available).
tray_controller_ = std::make_unique<FakeVideoConferenceTrayController>();
}
AshTestBase::SetUp();
widget_ = CreateFramelessTestWidget();
widget_->SetFullscreen(true);
}
void TearDown() override {
widget_.reset();
AshTestBase::TearDown();
if (IsVcDlcUiEnabled()) {
tray_controller_.reset();
}
}
void PressTab() {
ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
}
bool IsVcDlcUiEnabled() { return GetParam(); }
std::u16string GetExpectedDownloadPendingLabel() {
return l10n_util::GetStringUTF16(
IDS_ASH_FEATURE_TILE_DOWNLOAD_PENDING_TITLE);
}
std::u16string GetExpectedDownloadInProgressLabel(int progress) {
return l10n_util::GetStringFUTF16(
IDS_ASH_FEATURE_TILE_DOWNLOAD_IN_PROGRESS_TITLE,
base::NumberToString16(progress));
}
std::unique_ptr<views::Widget> widget_;
std::unique_ptr<FakeVideoConferenceTrayController> tray_controller_ = nullptr;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(IsVcDlcUiEnabled, FeatureTileTest, testing::Bool());
TEST_P(FeatureTileTest, PrimaryTile_LaunchSurface) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/false);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
EXPECT_FALSE(tile->is_icon_clickable());
EXPECT_FALSE(tile->drill_in_arrow());
// Ensure label hasn't been pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile);
// Ensure label was pressed and button does not toggle after clicking it.
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_FALSE(tile->IsToggled());
}
TEST_P(FeatureTileTest, PrimaryTile_Toggle) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/true);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
EXPECT_FALSE(tile->is_icon_clickable());
EXPECT_FALSE(tile->drill_in_arrow());
// Ensure label hasn't been pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile);
// Ensure label was pressed and button toggles after clicking it.
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_TRUE(tile->IsToggled());
LeftClickOn(tile);
// Ensure button toggles after clicking it again.
EXPECT_FALSE(tile->IsToggled());
}
TEST_P(FeatureTileTest, PrimaryTile_ToggleWithDrillIn) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/true);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
tile->SetIconClickable(true);
views::test::RunScheduledLayout(tile);
EXPECT_TRUE(tile->is_icon_clickable());
ASSERT_TRUE(tile->icon_button());
EXPECT_TRUE(tile->icon_button()->GetEnabled());
// Ensure tile is not toggled and icon is not pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasIconPressed());
LeftClickOn(tile);
// Clicking the tile does not press the icon.
EXPECT_FALSE(mock_controller->WasIconPressed());
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_FALSE(tile->IsToggled());
LeftClickOn(tile->icon_button());
// Clicking the icon presses it and toggles the tile.
EXPECT_TRUE(mock_controller->WasIconPressed());
EXPECT_TRUE(tile->IsToggled());
}
TEST_P(FeatureTileTest, PrimaryTile_SetIconClickable) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/true);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
// Ensure clickable state is correct.
tile->SetIconClickable(true);
EXPECT_TRUE(tile->is_icon_clickable());
EXPECT_TRUE(tile->icon_button()->GetEnabled());
auto* ink_drop = views::InkDrop::Get(tile->icon_button());
ASSERT_TRUE(ink_drop);
EXPECT_EQ(ink_drop->GetMode(), views::InkDropHost::InkDropMode::ON);
// Ensure icon button can take focus.
auto* focus_manager = widget_->GetFocusManager();
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
PressTab();
EXPECT_EQ(tile->icon_button(), focus_manager->GetFocusedView());
// Ensure button state changes when set to not clickable.
tile->SetIconClickable(false);
EXPECT_FALSE(tile->is_icon_clickable());
EXPECT_FALSE(tile->icon_button()->GetEnabled());
EXPECT_EQ(ink_drop->GetMode(), views::InkDropHost::InkDropMode::OFF);
// Ensure icon button doesn't focus.
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
}
TEST_P(FeatureTileTest, PrimaryTile_DecorativeDrillIn) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/false);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
tile->CreateDecorativeDrillInArrow();
views::test::RunScheduledLayout(tile);
EXPECT_FALSE(tile->is_icon_clickable());
ASSERT_TRUE(tile->drill_in_arrow());
EXPECT_TRUE(tile->drill_in_arrow()->GetVisible());
// Ensure label hasn't been pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile->drill_in_arrow());
// Ensure label was pressed and button does not toggle after clicking it.
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_FALSE(tile->IsToggled());
// Ensure drill-in button doesn't focus.
auto* focus_manager = widget_->GetFocusManager();
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
}
// Togglable tiles with a decorative drill-in button do not toggle when
// clicked, but show a detailed view from where the user can trigger an action
// which toggles the button state (e.g. selecting a VPN network).
// Since this test uses mock feature tiles and cannot easily create detailed
// views, this toggle behavior will be omitted.
TEST_P(FeatureTileTest, PrimaryTile_ToggleWithDecorativeDrillIn) {
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/true);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
tile->CreateDecorativeDrillInArrow();
views::test::RunScheduledLayout(tile);
EXPECT_FALSE(tile->is_icon_clickable());
ASSERT_TRUE(tile->drill_in_arrow());
EXPECT_TRUE(tile->drill_in_arrow()->GetVisible());
// Ensure label is not pressed.
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile);
// Ensure label was pressed after clicking it.
EXPECT_FALSE(mock_controller->WasIconPressed());
EXPECT_TRUE(mock_controller->WasLabelPressed());
LeftClickOn(tile->drill_in_arrow());
// Ensure `WasIconPressed` not pressed after clicking drill-in button.
EXPECT_FALSE(mock_controller->WasIconPressed());
// Ensure drill-in button doesn't focus.
auto* focus_manager = widget_->GetFocusManager();
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
PressTab();
EXPECT_EQ(tile, focus_manager->GetFocusedView());
}
TEST_P(FeatureTileTest, PrimaryTile_WithSubLabel) {
FeatureTile primary_tile_with_sub_label(views::Button::PressedCallback(),
/*is_togglable=*/true,
FeatureTile::TileType::kPrimary);
primary_tile_with_sub_label.SetLabel(u"Button label");
primary_tile_with_sub_label.SetSubLabel(u"Sub label");
EXPECT_EQ(primary_tile_with_sub_label.label()->GetHorizontalAlignment(),
gfx::ALIGN_LEFT);
EXPECT_EQ(primary_tile_with_sub_label.label()->GetMultiLine(), false);
EXPECT_EQ((int)primary_tile_with_sub_label.label()->GetMaxLines(), 0);
}
TEST_P(FeatureTileTest, PrimaryTile_UpdatedCornerRadius) {
int updated_radius = 10;
auto mock_controller =
std::make_unique<MockFeaturePodController>(/*togglable=*/false);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
// Verify the initial tile state utilizes the default radius.
views::RoundRectHighlightPathGenerator* path_generator =
static_cast<views::RoundRectHighlightPathGenerator*>(
tile->GetProperty(views::kHighlightPathGeneratorKey));
gfx::RectF bounds = gfx::RectF(tile->GetLocalBounds());
EXPECT_EQ(path_generator->GetRoundRect(bounds),
gfx::RRectF(bounds, kDefaultButtonRadius));
tile->SetButtonCornerRadius(updated_radius);
// Verify the tile utilizes the updated radius.
path_generator = static_cast<views::RoundRectHighlightPathGenerator*>(
tile->GetProperty(views::kHighlightPathGeneratorKey));
bounds = gfx::RectF(tile->GetLocalBounds());
EXPECT_EQ(path_generator->GetRoundRect(bounds),
gfx::RRectF(bounds, updated_radius));
}
TEST_P(FeatureTileTest, CompactTile_AddedAndRemoveSubLabel) {
// Create initial compact `FeatureTile` without a sub-label and verify default
// parameters.
FeatureTile compact_tile_with_sub_label(views::Button::PressedCallback(),
/*is_togglable=*/true,
FeatureTile::TileType::kCompact);
compact_tile_with_sub_label.SetLabel(u"Button label");
EXPECT_FALSE(compact_tile_with_sub_label.sub_label()->GetVisible());
EXPECT_EQ(compact_tile_with_sub_label.label()->GetHorizontalAlignment(),
gfx::ALIGN_CENTER);
EXPECT_EQ(compact_tile_with_sub_label.label()->GetMultiLine(), true);
EXPECT_EQ((int)compact_tile_with_sub_label.label()->GetMaxLines(), 2);
// Add a sub-label, update visibility, and verify parameters are updated.
compact_tile_with_sub_label.SetSubLabel(u"Sub label");
compact_tile_with_sub_label.SetSubLabelVisibility(true);
EXPECT_EQ(compact_tile_with_sub_label.label()->GetText(), u"Button label");
EXPECT_EQ(compact_tile_with_sub_label.label()->GetHorizontalAlignment(),
gfx::ALIGN_CENTER);
EXPECT_EQ(compact_tile_with_sub_label.label()->GetMultiLine(), false);
EXPECT_EQ((int)compact_tile_with_sub_label.label()->GetMaxLines(), 1);
EXPECT_TRUE(compact_tile_with_sub_label.sub_label()->GetVisible());
// Hide sub-label and verify parameters are back to defaults.
compact_tile_with_sub_label.SetSubLabelVisibility(false);
EXPECT_FALSE(compact_tile_with_sub_label.sub_label()->GetVisible());
EXPECT_EQ(compact_tile_with_sub_label.label()->GetHorizontalAlignment(),
gfx::ALIGN_CENTER);
EXPECT_EQ(compact_tile_with_sub_label.label()->GetMultiLine(), true);
EXPECT_EQ((int)compact_tile_with_sub_label.label()->GetMaxLines(), 2);
}
TEST_P(FeatureTileTest, CompactTile_LaunchSurface) {
auto mock_controller = std::make_unique<MockFeaturePodController>(
/*togglable=*/false);
auto* tile =
widget_->SetContentsView(mock_controller->CreateTile(/*compact=*/true));
EXPECT_FALSE(tile->is_icon_clickable());
// Ensure label hasn't been pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile);
// Ensure label was pressed and button does not toggle after clicking it.
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_FALSE(tile->IsToggled());
}
TEST_P(FeatureTileTest, CompactTile_Toggle) {
auto mock_controller = std::make_unique<MockFeaturePodController>(
/*togglable=*/true);
auto* tile =
widget_->SetContentsView(mock_controller->CreateTile(/*compact=*/true));
EXPECT_FALSE(tile->is_icon_clickable());
// Ensure label hasn't been pressed.
EXPECT_FALSE(tile->IsToggled());
EXPECT_FALSE(mock_controller->WasLabelPressed());
LeftClickOn(tile);
// Ensure label was pressed and button toggles after clicking it.
EXPECT_TRUE(mock_controller->WasLabelPressed());
EXPECT_TRUE(tile->IsToggled());
// Ensure button toggles after clicking it again.
LeftClickOn(tile);
EXPECT_FALSE(tile->IsToggled());
}
TEST_P(FeatureTileTest, TogglingTileUpdatesInkDropColor) {
auto* tile = widget_->SetContentsView(
std::make_unique<FeatureTile>(views::Button::PressedCallback()));
auto* color_provider = tile->GetColorProvider();
tile->SetToggled(true);
EXPECT_EQ(views::InkDrop::Get(tile)->GetBaseColor(),
color_provider->GetColor(cros_tokens::kCrosSysRipplePrimary));
tile->SetToggled(false);
EXPECT_EQ(
views::InkDrop::Get(tile)->GetBaseColor(),
color_provider->GetColor(cros_tokens::kCrosSysRippleNeutralOnSubtle));
}
// Regression test for http://b/284318391
TEST_P(FeatureTileTest, TogglingTileHidesInkDrop) {
auto mock_controller = std::make_unique<MockFeaturePodController>(
/*togglable=*/true);
auto* tile = widget_->SetContentsView(mock_controller->CreateTile());
LeftClickOn(tile);
ASSERT_TRUE(tile->IsToggled());
EXPECT_EQ(views::InkDrop::Get(tile)->GetInkDrop()->GetTargetInkDropState(),
views::InkDropState::HIDDEN);
}
TEST_P(FeatureTileTest, AccessibilityRoles) {
// Togglable feature tiles (like Do Not Disturb) have role "toggle button".
FeatureTile togglable_tile(views::Button::PressedCallback(),
/*is_togglable=*/true);
togglable_tile.SetToggled(true);
ui::AXNodeData node_data;
togglable_tile.GetViewAccessibility().GetAccessibleNodeData(&node_data);
EXPECT_EQ(node_data.role, ax::mojom::Role::kToggleButton);
EXPECT_EQ(node_data.GetCheckedState(), ax::mojom::CheckedState::kTrue);
togglable_tile.SetToggled(false);
ui::AXNodeData node_data2;
togglable_tile.GetViewAccessibility().GetAccessibleNodeData(&node_data2);
EXPECT_EQ(node_data2.role, ax::mojom::Role::kToggleButton);
EXPECT_EQ(node_data2.GetCheckedState(), ax::mojom::CheckedState::kFalse);
// However, togglable feature tiles that have a clickable icon (like Network
// and Bluetooth) do not have role "toggle button", since clicking the main
// tile takes the user to a detail page.
togglable_tile.SetIconClickable(true);
ui::AXNodeData node_data3;
togglable_tile.GetViewAccessibility().GetAccessibleNodeData(&node_data3);
EXPECT_EQ(node_data3.role, ax::mojom::Role::kButton);
// Non-togglable feature tiles are just buttons.
FeatureTile non_togglable_tile(views::Button::PressedCallback(),
/*is_togglable=*/false);
ui::AXNodeData node_data4;
non_togglable_tile.GetViewAccessibility().GetAccessibleNodeData(&node_data4);
EXPECT_EQ(node_data4.role, ax::mojom::Role::kButton);
}
// Tests that the tile's label and tooltip are set according to the feature tile
// DLC's download state.
TEST_P(FeatureTileTest, DownloadLabelAndTooltip) {
// Download states are only supported when `VcDlcUi` is enabled.
if (!IsVcDlcUiEnabled()) {
return;
}
// Create a tile and verify that it has its client-specified label by default.
std::u16string client_specified_label(u"Client Specified Label");
std::u16string client_specified_tooltip(u"Client Specified Tooltip");
FeatureTile tile(views::Button::PressedCallback(),
/*is_togglable=*/true);
tile.SetLabel(client_specified_label);
tile.SetTooltipText(client_specified_tooltip);
EXPECT_EQ(client_specified_label, tile.label()->GetText());
EXPECT_EQ(client_specified_tooltip, tile.GetTooltipText());
// Set the tile to have a pending download and verify that the label and
// tooltip are updated.
tile.SetDownloadState(FeatureTile::DownloadState::kPending, /*progress=*/0);
EXPECT_EQ(GetExpectedDownloadPendingLabel(), tile.label()->GetText());
EXPECT_EQ(GetExpectedDownloadPendingLabel(), tile.GetTooltipText());
// Set the tile to have an in-progress download and verify that the label is
// updated accordingly.
tile.SetDownloadState(FeatureTile::DownloadState::kDownloading,
/*progress=*/7);
EXPECT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.label()->GetText());
EXPECT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.GetTooltipText());
// Set the tile to have a successfully-completed download and verify that the
// label is set to the client-specified label.
tile.SetDownloadState(FeatureTile::DownloadState::kDownloaded,
/*progress=*/0);
EXPECT_EQ(client_specified_label, tile.label()->GetText());
EXPECT_EQ(client_specified_tooltip, tile.GetTooltipText());
// Set the tile to have an error with its download and verify that the label
// is set to the client-specified label with additional info.
tile.SetDownloadState(FeatureTile::DownloadState::kError, /*progress=*/0);
EXPECT_EQ(client_specified_label, tile.label()->GetText());
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_FEATURE_TILE_DOWNLOAD_ERROR,
client_specified_label),
tile.GetTooltipText());
// Set the tile to have no associated download and verify that the label is
// set to the client-specified label.
tile.SetDownloadState(FeatureTile::DownloadState::kNone, /*progress=*/0);
EXPECT_EQ(client_specified_label, tile.label()->GetText());
EXPECT_EQ(client_specified_tooltip, tile.GetTooltipText());
}
// Tests that updates to the tile's client-specified label and tooltip are
// delayed until the current download is finished.
TEST_P(FeatureTileTest, LabelAndTooltipUpdatesDelayedDuringDownload) {
// Download states are only supported when `VcDlcUi` is enabled.
if (!IsVcDlcUiEnabled()) {
return;
}
// Create a tile and set it to have an in-progress download.
std::u16string label_1(u"Client Specified Label");
std::u16string tooltip_1(u"Client Specified Tooltip");
FeatureTile tile(views::Button::PressedCallback(),
/*is_togglable=*/true);
tile.SetLabel(label_1);
tile.SetTooltipText(tooltip_1);
tile.SetDownloadState(FeatureTile::DownloadState::kDownloading,
/*progress=*/7);
ASSERT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.label()->GetText());
ASSERT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.GetTooltipText());
// Change the tile's client-specified label and tooltip, and verify that the
// change is not yet reflected due to the on-going download.
std::u16string label_2(u"New Label");
std::u16string tooltip_2(u"New Tooltip");
tile.SetLabel(label_2);
tile.SetTooltipText(tooltip_2);
EXPECT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.label()->GetText());
EXPECT_EQ(GetExpectedDownloadInProgressLabel(/*progress=*/7),
tile.GetTooltipText());
// Set the tile's download to be finished and verify that the tile now has the
// new client-specified label.
tile.SetDownloadState(FeatureTile::DownloadState::kDownloaded,
/*progress=*/0);
EXPECT_EQ(label_2, tile.label()->GetText());
EXPECT_EQ(tooltip_2, tile.GetTooltipText());
// Set the tile to have a pending download.
tile.SetDownloadState(FeatureTile::DownloadState::kPending, /*progress=*/0);
ASSERT_EQ(GetExpectedDownloadPendingLabel(), tile.label()->GetText());
// Change the tile's client-specified label and verify that the change is not
// yet reflected due to the pending download.
std::u16string label_3(u"Another new label");
std::u16string tooltip_3(u"Another new label");
tile.SetLabel(label_3);
tile.SetTooltipText(tooltip_3);
EXPECT_EQ(GetExpectedDownloadPendingLabel(), tile.label()->GetText());
EXPECT_EQ(GetExpectedDownloadPendingLabel(), tile.GetTooltipText());
// Set the tile's download to be finished (with an error this time, for
// for variety) and verify that the tile now has the new client-specified
// label, but the tooltip shows the error message.
tile.SetDownloadState(FeatureTile::DownloadState::kError, /*progress=*/0);
EXPECT_EQ(label_3, tile.label()->GetText());
EXPECT_EQ(
l10n_util::GetStringFUTF16(IDS_ASH_FEATURE_TILE_DOWNLOAD_ERROR, label_3),
tile.GetTooltipText());
}
} // namespace ash