// 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 <optional>
#include <string>
#include "ash/ash_element_identifiers.h"
#include "base/check.h"
#include "base/test/scoped_run_loop_timeout.h"
#include "base/time/time.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/ash/interactive/cellular/cellular_util.h"
#include "chrome/test/base/ash/interactive/cellular/esim_interactive_uitest_base.h"
#include "chrome/test/base/ash/interactive/cellular/wait_for_service_connected_observer.h"
#include "chrome/test/base/ash/interactive/network/shill_device_power_state_observer.h"
#include "chrome/test/base/ash/interactive/settings/interactive_uitest_elements.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/network/network_type_pattern.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/polling_state_observer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/event.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/interaction/polling_view_observer.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/view.h"
namespace ash {
namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOSSettingsId);
class EsimInteractiveUiTest : public EsimInteractiveUiTestBase {
protected:
// InteractiveAshTest:
void SetUpOnMainThread() override {
EsimInteractiveUiTestBase::SetUpOnMainThread();
esim_info_ = std::make_unique<SimInfo>(/*id=*/0);
ConfigureEsimProfile(euicc_info(), *esim_info_, /*connected=*/true);
}
const SimInfo& esim_info() const { return *esim_info_; }
private:
std::unique_ptr<SimInfo> esim_info_;
};
IN_PROC_BROWSER_TEST_F(EsimInteractiveUiTest,
OpenAddEsimDialogFromQuickSettings) {
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ShillDevicePowerStateObserver,
kMobileDataPoweredState);
using Observer = views::test::PollingViewObserver<bool, views::View>;
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(Observer, kPollingViewState);
bool has_clicked_add_esim_entry = false;
RunTestSequence(
Log("Waiting for cellular to be enabled"),
ObserveState(
kMobileDataPoweredState,
std::make_unique<ShillDevicePowerStateObserver>(
ShillManagerClient::Get(), NetworkTypePattern::Mobile())),
WaitForState(kMobileDataPoweredState, true),
Log("Opening Quick Settings and navigating to the network page"),
OpenQuickSettings(), NavigateQuickSettingsToNetworkPage(),
Log("Waiting for the 'add eSIM' button to be visible, then clicking it"),
InstrumentNextTab(kOSSettingsId, AnyBrowser()),
// The views in the network page of Quick Settings (that are not
// top-level e.g. the toggles or headers) are prone to frequent
// re-ordering and/or can rapidly appear/disappear before being stable due
// to network state changes. Instead of attempting to click the view via
// moving and clicking the mouse we instead click via code to avoid the
// possibility of the element disappearing during the step.
PollView(
kPollingViewState, kNetworkAddEsimElementId,
[&has_clicked_add_esim_entry](const views::View* view) -> bool {
if (!has_clicked_add_esim_entry) {
views::test::ButtonTestApi(
views::Button::AsButton(const_cast<views::View*>(view)))
.NotifyClick(ui::MouseEvent(ui::EventType::kMousePressed,
gfx::PointF(), gfx::PointF(),
base::TimeTicks(), 0, 0));
}
has_clicked_add_esim_entry = true;
return true;
},
base::Milliseconds(50)),
// The `WaitForState` step also requires that the element in question
// exists for the duration of the step. As mentioned above, the element
// may rapidly appear/disappear which would cause `WaitForState` to fail.
// Instead, we wait for the Quick Settings to close as a result of the
// button being clicked.
WaitForHide(ash::kQuickSettingsViewElementId),
Log("Waiting for OS Settings to open"),
InAnyContext(WaitForShow(kOSSettingsId)));
ui::ElementContext context = FindSystemWebApp(SystemWebAppType::SETTINGS);
// Run the remaining steps with a longer timeout since it can take more than
// 10 seconds for OS Settings to open.
const base::test::ScopedRunLoopTimeout longer_timeout(FROM_HERE,
base::Seconds(15));
// Run the following steps with the OS Settings context set as the default.
RunTestSequenceInContext(
context,
Log("Waiting for OS Settings to navigate to cellular subpage"),
WaitForElementTextContains(
kOSSettingsId, settings::InternetSettingsSubpageTitle(),
/*text=*/l10n_util::GetStringUTF8(IDS_NETWORK_TYPE_MOBILE_DATA)),
Log("Waiting for 'add eSIM' dialog to open"),
WaitForElementTextContains(
kOSSettingsId, settings::cellular::EsimDialogTitle(),
/*text=*/
l10n_util::GetStringUTF8(
IDS_CELLULAR_SETUP_ESIM_PAGE_PROFILE_DISCOVERY_CONSENT_TITLE)),
Do([&]() { CloseSystemWebApp(SystemWebAppType::SETTINGS); }),
Log("Test complete"));
}
IN_PROC_BROWSER_TEST_F(EsimInteractiveUiTest, AutoconnectBehavior) {
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ShillDevicePowerStateObserver,
kMobileDataPoweredState);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(WaitForServiceConnectedObserver,
kCellularServiceConnected);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
kCellularServiceAutoconnect);
ui::ElementContext context =
LaunchSystemWebApp(SystemWebAppType::SETTINGS, kOSSettingsId);
// Run the following steps with the OS Settings context set as the default.
RunTestSequenceInContext(
context,
Log("Waiting for cellular to be enabled"),
ObserveState(
kMobileDataPoweredState,
std::make_unique<ShillDevicePowerStateObserver>(
ShillManagerClient::Get(), NetworkTypePattern::Mobile())),
WaitForState(kMobileDataPoweredState, true),
Log("Waiting for cellular network to be connected"),
ObserveState(kCellularServiceConnected,
std::make_unique<WaitForServiceConnectedObserver>(
esim_info().iccid())),
WaitForState(kCellularServiceConnected, true),
Log("Waiting for cellular network to be have auto-connect enabled"),
PollState(kCellularServiceAutoconnect,
[this]() -> bool {
const auto* shill_service_client_test =
ShillServiceClient::Get()->GetTestInterface();
CHECK(shill_service_client_test);
const auto* cellular_properties =
shill_service_client_test->GetServiceProperties(
esim_info().service_path());
CHECK(cellular_properties);
const std::optional<bool> autoconnect =
cellular_properties->FindBool(
shill::kAutoConnectProperty);
CHECK(autoconnect.has_value());
return *autoconnect;
}),
WaitForState(kCellularServiceAutoconnect, true),
Log("Navigating to the details page for the cellular network"),
NavigateToInternetDetailsPage(kOSSettingsId,
NetworkTypePattern::Cellular(),
esim_info().nickname()),
WaitForElementTextContains(kOSSettingsId,
settings::InternetSettingsSubpageTitle(),
/*text=*/esim_info().nickname()),
Log("Disabling auto-connect for the cellular network"),
WaitForElementEnabled(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
WaitForElementChecked(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
ClickElement(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
WaitForElementUnchecked(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
WaitForState(kCellularServiceAutoconnect, false),
WaitForElementWithManagedPropertyBoolean(
kOSSettingsId, settings::InternetDetailsSubpage(),
/*property=*/"typeProperties.cellular.autoConnect",
/*expected_value=*/false),
Log("Enabling auto-connect for the cellular network"),
ClickElement(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
WaitForElementChecked(
kOSSettingsId,
settings::cellular::CellularDetailsSubpageAutoConnectToggle()),
WaitForState(kCellularServiceAutoconnect, true),
Log("Closing the Settings app"),
Do([&]() { CloseSystemWebApp(SystemWebAppType::SETTINGS); }),
Log("Test complete"));
}
IN_PROC_BROWSER_TEST_F(EsimInteractiveUiTest, ConnectDisconnect) {
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(WaitForServiceConnectedObserver,
kCellularServiceConnected);
ui::ElementContext context =
LaunchSystemWebApp(SystemWebAppType::SETTINGS, kOSSettingsId);
// Run the following steps with the OS Settings context set as the default.
RunTestSequenceInContext(
context,
ObserveState(kCellularServiceConnected,
std::make_unique<WaitForServiceConnectedObserver>(
esim_info().iccid())),
WaitForState(kCellularServiceConnected, true),
Log("Navigating to the details page for the eSIM network"),
NavigateToInternetDetailsPage(kOSSettingsId,
NetworkTypePattern::Cellular(),
esim_info().nickname()),
Log("Disconnect eSIM network"),
WaitForElementExists(kOSSettingsId,
settings::SettingsSubpageConnectDisconnectButton()),
ClickElement(kOSSettingsId,
settings::SettingsSubpageConnectDisconnectButton()),
WaitForState(kCellularServiceConnected, false),
Log("Connect to eSIM network"),
WaitForElementExists(kOSSettingsId,
settings::SettingsSubpageConnectDisconnectButton()),
ClickElement(kOSSettingsId,
settings::SettingsSubpageConnectDisconnectButton()),
WaitForState(kCellularServiceConnected, true),
Log("Test complete"));
}
} // namespace
} // namespace ash