// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/scalable_iph/scalable_iph.h"
#include <string_view>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/game_dashboard/game_dashboard_controller.h"
#include "ash/public/cpp/app_list/app_list_controller.h"
#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "ash/public/cpp/multi_user_window_manager.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/system/anchored_nudge_manager.h"
#include "ash/public/cpp/test/app_list_test_api.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "ash/test/test_widget_builder.h"
#include "base/feature_list.h"
#include "base/scoped_observation.h"
#include "base/strings/pattern.h"
#include "chrome/browser/apps/app_service/app_registry_cache_waiter.h"
#include "chrome/browser/ash/app_list/app_list_client_impl.h"
#include "chrome/browser/ash/app_list/app_list_model_updater.h"
#include "chrome/browser/ash/app_list/test/chrome_app_list_test_support.h"
#include "chrome/browser/ash/login/lock/screen_locker_tester.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/ui/user_adding_screen.h"
#include "chrome/browser/ash/login/wizard_controller.h"
#include "chrome/browser/ash/printing/cups_print_job.h"
#include "chrome/browser/ash/printing/cups_print_job_manager.h"
#include "chrome/browser/ash/printing/cups_print_job_manager_factory.h"
#include "chrome/browser/ash/printing/synced_printers_manager.h"
#include "chrome/browser/ash/printing/synced_printers_manager_factory.h"
#include "chrome/browser/ash/scalable_iph/customizable_test_env_browser_test_base.h"
#include "chrome/browser/ash/scalable_iph/scalable_iph_browser_test_base.h"
#include "chrome/browser/ash/scalable_iph/scalable_iph_delegate_impl.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/scalable_iph/scalable_iph_factory.h"
#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/ash/components/phonehub/fake_feature_status_provider.h"
#include "chromeos/ash/components/phonehub/feature_status.h"
#include "chromeos/ash/components/scalable_iph/iph_session.h"
#include "chromeos/ash/components/scalable_iph/scalable_iph_constants.h"
#include "chromeos/ash/components/scalable_iph/scalable_iph_delegate.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/printing/printer_configuration.h"
#include "components/account_id/account_id.h"
#include "components/feature_engagement/test/mock_tracker.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/variations/service/variations_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "extensions/common/constants.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/event_generator_delegate_aura.h"
#include "ui/aura/window.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/public/cpp/notification.h"
namespace {
using ScalableIphBrowserTest = ::ash::ScalableIphBrowserTestBase;
using TestEnvironment =
::ash::CustomizableTestEnvBrowserTestBase::TestEnvironment;
using UserSessionType =
::ash::CustomizableTestEnvBrowserTestBase::UserSessionType;
constexpr char kTestLogMessage[] = "test-log-message";
constexpr char kTestLogMessagePattern[] = "*test-log-message*";
constexpr char kScalableIphDebugLogTextUrl[] =
"chrome-untrusted://scalable-iph-debug/log.txt";
constexpr char16_t kTestGameWindowTitle[] = u"ScalableIphTestGameWindow";
BASE_FEATURE(kScalableIphTestTwo,
"ScalableIphTestTwo",
base::FEATURE_DISABLED_BY_DEFAULT);
void OverrideStoredPermanentCountry(std::string_view country_code) {
CHECK(g_browser_process->variations_service()->OverrideStoredPermanentCountry(
std::string(country_code)));
}
bool IsGoogleChrome() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
return true;
#else
return false;
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
void LockAndUnlockSession() {
const AccountId account_id =
user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
ash::ScreenLockerTester tester;
tester.Lock();
EXPECT_TRUE(tester.IsLocked());
tester.SetUnlockPassword(account_id, "pass");
tester.UnlockWithPassword(account_id, "pass");
tester.WaitForUnlock();
EXPECT_FALSE(tester.IsLocked());
}
void SendSuspendDone() {
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
power_manager::SuspendImminent::IDLE);
chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
}
std::unique_ptr<aura::Window> CreateAuraWindow(std::u16string window_title) {
ash::TestWidgetBuilder builder;
builder.SetWindowTitle(window_title);
builder.SetTestWidgetDelegate();
builder.SetContext(ash::Shell::GetPrimaryRootWindow());
builder.SetBounds(gfx::Rect(0, 0, 600, 400));
views::Widget* widget = builder.BuildOwnedByNativeWidget();
return std::unique_ptr<aura::Window>(widget->GetNativeWindow());
}
class IsOnlineValueWaiter {
public:
IsOnlineValueWaiter(scalable_iph::ScalableIphDelegate* scalable_iph_delegate,
bool expected)
: scalable_iph_delegate_(scalable_iph_delegate), expected_(expected) {}
void Wait() {
if (scalable_iph_delegate_->IsOnline() == expected_) {
return;
}
repeating_timer_.Start(FROM_HERE, base::Milliseconds(10),
base::BindRepeating(&IsOnlineValueWaiter::Check,
weak_ptr_factory_.GetWeakPtr()));
run_loop_.Run();
}
private:
void Check() {
if (scalable_iph_delegate_->IsOnline() != expected_) {
return;
}
run_loop_.Quit();
}
raw_ptr<scalable_iph::ScalableIphDelegate> scalable_iph_delegate_;
bool expected_;
base::RepeatingTimer repeating_timer_;
base::RunLoop run_loop_;
base::WeakPtrFactory<IsOnlineValueWaiter> weak_ptr_factory_{this};
};
class AppListItemWaiter : public AppListModelUpdaterObserver {
public:
AppListItemWaiter(std::string app_id,
AppListModelUpdater* app_list_model_updater)
: app_id_(app_id), app_list_model_updater_(app_list_model_updater) {}
void Wait() {
if (app_list_model_updater_->FindItem(app_id_)) {
return;
}
app_list_model_updater_observation_.Observe(app_list_model_updater_);
run_loop_.Run();
}
void OnAppListItemAdded(ChromeAppListItem* item) override {
if (item->id() == app_id_) {
run_loop_.Quit();
}
}
private:
const std::string app_id_;
raw_ptr<AppListModelUpdater> app_list_model_updater_;
base::RunLoop run_loop_;
base::ScopedObservation<AppListModelUpdater, AppListModelUpdaterObserver>
app_list_model_updater_observation_{this};
};
class CupsPrintJobManagerWaiter : public ash::CupsPrintJobManager::Observer {
public:
CupsPrintJobManagerWaiter(ash::CupsPrintJobManager* print_job_manager,
int job_id)
: print_job_manager_(print_job_manager), job_id_(job_id) {
CHECK(print_job_manager_);
print_job_manager_observation_.Observe(print_job_manager_);
}
void Wait() { run_loop_.Run(); }
void OnPrintJobCreated(base::WeakPtr<ash::CupsPrintJob> job) override {
if (job->job_id() == job_id_) {
CHECK(run_loop_.IsRunningOnCurrentThread())
<< "Observed expected print job id before run_loop_ is running: "
<< job_id_;
run_loop_.Quit();
}
}
private:
base::ScopedObservation<ash::CupsPrintJobManager,
ash::CupsPrintJobManager::Observer>
print_job_manager_observation_{this};
raw_ptr<ash::CupsPrintJobManager> print_job_manager_;
base::RunLoop run_loop_;
int job_id_;
};
class ScalableIphBrowserTestFlagOff
: public ash::CustomizableTestEnvBrowserTestBase {
public:
ScalableIphBrowserTestFlagOff() {
scoped_feature_list_.InitAndDisableFeature(ash::features::kScalableIph);
scalable_iph::ScalableIph::ForceEnableIphFeatureForTesting();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class ScalableIphBrowserTestNoIph
: public ash::CustomizableTestEnvBrowserTestBase {
public:
ScalableIphBrowserTestNoIph() {
// Disable all IPH feature flags to avoid one of them get enabled by
// fieldtrial testing config. Fieldtrial testing config won't flip a
// flag if it's already force-enabled/disabled. Convert it to a vector
// of `FeatureRef`. `ScalableIph` cannot depend on it as it's in base::test.
std::vector<base::test::FeatureRef> disabled_features_refs;
const std::vector<raw_ptr<const base::Feature, VectorExperimental>>&
disabled_features =
scalable_iph::ScalableIph::GetFeatureListConstantForTesting();
for (auto feature : disabled_features) {
disabled_features_refs.push_back(base::test::FeatureRef(*feature));
}
scoped_feature_list_.InitWithFeatures({ash::features::kScalableIph},
disabled_features_refs);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class ScalableIphBrowserTestGame : public ScalableIphBrowserTest {
public:
void AppendTestSpecificFeatures(
std::vector<base::test::FeatureRefAndParams>& enabled_features,
std::vector<base::test::FeatureRef>& disabled_features) override {
enabled_features.push_back(
base::test::FeatureRefAndParams(ash::features::kGameDashboard, {}));
}
void SetUpOnMainThread() override {
ScalableIphBrowserTest::SetUpOnMainThread();
CHECK(ash::GameDashboardController::Get())
<< "Game dashboard has to be enabled for this test.";
}
};
class ScalableIphBrowserTestGameMultiUser : public ScalableIphBrowserTestGame {
public:
ScalableIphBrowserTestGameMultiUser() { enable_multi_user_ = true; }
};
class ScalableIphBrowserTestDebugOff : public ScalableIphBrowserTest {
public:
ScalableIphBrowserTestDebugOff() { enable_scalable_iph_debug_ = false; }
};
class ScalableIphBrowserTestFeatureOffDebugOn : public ScalableIphBrowserTest {
public:
ScalableIphBrowserTestFeatureOffDebugOn() {
enable_scalable_iph_ = false;
setup_scalable_iph_ = false;
CHECK(enable_scalable_iph_debug_)
<< "Debug feature is on by default for ScalableIphBrowserTest";
}
};
class ScalableIphBrowserTestPreinstallApps : public ScalableIphBrowserTest {
public:
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
ScalableIphBrowserTest::SetUpDefaultCommandLine(command_line);
command_line->RemoveSwitch(switches::kDisableDefaultApps);
command_line->AppendSwitch(
ash::switches::kAllowDefaultShelfPinLayoutIgnoringSync);
}
};
class ScalableIphBrowserTestHelpApp
: public ScalableIphBrowserTestPreinstallApps {
public:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params;
AppendVersionNumber(params);
base::test::FeatureRefAndParams test_config(TestIphFeature(), params);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
base::test::FeatureRefAndParams help_app_feature(
ash::features::kHelpAppWelcomeTips, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, help_app_feature, test_config}, {});
}
void SetUpOnMainThread() override {
ScalableIphBrowserTestPreinstallApps::SetUpOnMainThread();
ash::SystemWebAppManager::GetForTest(browser()->profile())
->InstallSystemAppsForTesting();
}
};
class ScalableIphBrowserTestHelpAppParameterized
: public ScalableIphBrowserTestHelpApp,
public testing::WithParamInterface<TestEnvironment> {
public:
void SetUp() override {
SetTestEnvironment(GetParam());
setup_scalable_iph_ = false;
ScalableIphBrowserTestHelpApp::SetUp();
}
};
class ScalableIphBrowserTestPerksMinecraftRealms
: public ScalableIphBrowserTest {
protected:
void AppendUiParams(base::FieldTrialParams& params) override {
ScalableIphBrowserTest::AppendFakeUiParamsNotification(
params, /*has_body_text=*/false, TestIphFeature());
params[FullyQualified(TestIphFeature(),
scalable_iph::kCustomButtonActionTypeParamName)] =
scalable_iph::kActionTypeOpenChromebookPerksMinecraftRealms2023;
}
};
class PerksEnvironment {
public:
PerksEnvironment(std::string_view country_code, const GURL& perks_url)
: country_code_(country_code), perks_url_(perks_url) {}
const std::string& country_code() const { return country_code_; }
GURL perks_url() const { return perks_url_; }
static std::string GenerateTestName(
testing::TestParamInfo<PerksEnvironment> test_param_info) {
const PerksEnvironment& param = test_param_info.param;
return param.country_code();
}
private:
const std::string country_code_;
const GURL perks_url_;
};
class ScalableIphBrowserTestPerksMinecraftRealmsParameterized
: public ScalableIphBrowserTestPerksMinecraftRealms,
public testing::WithParamInterface<PerksEnvironment> {};
class ScalableIphBrowserTestOobe : public ScalableIphBrowserTest {
public:
ScalableIphBrowserTestOobe() {
SetTestEnvironment(TestEnvironment(
ash::DeviceStateMixin::State::BEFORE_OOBE,
CustomizableTestEnvBrowserTestBase::UserSessionType::kRegularWithOobe));
}
};
class ScalableIphBrowserTestVersionNumberNoValue
: public ScalableIphBrowserTest {
protected:
void AppendVersionNumber(base::FieldTrialParams& params) override {}
};
class ScalableIphBrowserTestVersionNumberIncorrect
: public ScalableIphBrowserTest {
protected:
void AppendVersionNumber(base::FieldTrialParams& params) override {
ScalableIphBrowserTest::AppendVersionNumber(
params, TestIphFeature(),
base::NumberToString(scalable_iph::kCurrentVersionNumber - 1));
}
};
class ScalableIphBrowserTestVersionNumberInvalid
: public ScalableIphBrowserTest {
protected:
void AppendVersionNumber(base::FieldTrialParams& params) override {
ScalableIphBrowserTest::AppendVersionNumber(params, TestIphFeature(),
"Invalid");
}
};
class ScalableIphBrowserTestMultipleIphs : public ScalableIphBrowserTest {
protected:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params_one;
AppendVersionNumber(params_one, TestIphFeature());
AppendFakeUiParamsNotification(params_one,
/*has_body_text=*/true, TestIphFeature());
base::test::FeatureRefAndParams test_config_one(TestIphFeature(),
params_one);
base::FieldTrialParams params_two;
AppendVersionNumber(params_two, kScalableIphTestTwo);
AppendFakeUiParamsNotification(params_two,
/*has_body_text=*/true, kScalableIphTestTwo);
base::test::FeatureRefAndParams test_config_two(kScalableIphTestTwo,
params_two);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, test_config_one, test_config_two}, {});
}
};
class ScalableIphBrowserTestCustomConditionBase
: public ScalableIphBrowserTest {
protected:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params;
AppendVersionNumber(params);
AppendUiParams(params);
AppendCustomCondition(params);
base::test::FeatureRefAndParams test_config(TestIphFeature(), params);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
base::test::FeatureRefAndParams scalable_iph_debug_feature(
ash::features::kScalableIphDebug, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, scalable_iph_debug_feature, test_config}, {});
}
virtual void AppendCustomCondition(base::FieldTrialParams& params) = 0;
};
class ScalableIphBrowserTestTriggerEvent
: public ScalableIphBrowserTestCustomConditionBase {
protected:
void AppendCustomCondition(base::FieldTrialParams& params) override {
params[FullyQualified(
TestIphFeature(),
scalable_iph::kCustomConditionTriggerEventParamName)] =
scalable_iph::kEventNameUnlocked;
}
};
class ScalableIphBrowserTestNetworkConnection
: public ScalableIphBrowserTestCustomConditionBase {
protected:
void AppendCustomCondition(base::FieldTrialParams& params) override {
params[FullyQualified(
TestIphFeature(),
scalable_iph::kCustomConditionNetworkConnectionParamName)] =
scalable_iph::kCustomConditionNetworkConnectionOnline;
}
};
class ScalableIphBrowserTestNetworkConnectionOnline
: public ScalableIphBrowserTestNetworkConnection {
protected:
void SetUpOnMainThread() override {
AddOnlineNetwork();
ScalableIphBrowserTestNetworkConnection::SetUpOnMainThread();
// There is an async call for querying network status.
ash::ScalableIphDelegateImpl* scalable_iph_delegate_impl =
static_cast<ash::ScalableIphDelegateImpl*>(
mock_delegate()->fake_delegate());
IsOnlineValueWaiter is_online_value_waiter(scalable_iph_delegate_impl,
true);
is_online_value_waiter.Wait();
}
};
class ScalableIphBrowserTestClientAgeBase
: public ScalableIphBrowserTestCustomConditionBase {
protected:
void AppendCustomCondition(base::FieldTrialParams& params) override {
params[FullyQualified(
TestIphFeature(),
scalable_iph::kCustomConditionClientAgeInDaysParamName)] =
GetClientAgeTestValue();
}
void SetUpOnMainThread() override {
ScalableIphBrowserTest::SetUpOnMainThread();
mock_delegate()->FakeClientAgeInDays();
}
virtual std::string GetClientAgeTestValue() = 0;
};
class ScalableIphBrowserTestClientAgeZero
: public ScalableIphBrowserTestClientAgeBase {
protected:
// Day 0 is from 0 hours to 24 hours.
std::string GetClientAgeTestValue() override { return "0"; }
};
class ScalableIphBrowserTestClientAgeNonZero
: public ScalableIphBrowserTestClientAgeBase {
protected:
// Day 1 is from 24 hours to 48 hours.
std::string GetClientAgeTestValue() override { return "1"; }
};
class ScalableIphBrowserTestClientAgeInvalidString
: public ScalableIphBrowserTestClientAgeBase {
protected:
std::string GetClientAgeTestValue() override { return "abc"; }
};
class ScalableIphBrowserTestClientAgeInvalidNumber
: public ScalableIphBrowserTestClientAgeBase {
protected:
std::string GetClientAgeTestValue() override { return "-1"; }
};
class ScalableIphBrowserTestHasSavedPrinters
: public ScalableIphBrowserTestCustomConditionBase {
protected:
void AppendCustomCondition(base::FieldTrialParams& params) override {
params[FullyQualified(
TestIphFeature(),
scalable_iph::kCustomConditionHasSavedPrintersParamName)] =
scalable_iph::kCustomConditionHasSavedPrintersValueFalse;
}
};
class ScalableIphBrowserTestPhoneHubOnboardingEligible
: public ScalableIphBrowserTestCustomConditionBase {
public:
void SetUpOnMainThread() override {
ScalableIphBrowserTestCustomConditionBase::SetUpOnMainThread();
ash::ScalableIphDelegateImpl* scalable_iph_delegate_impl =
static_cast<ash::ScalableIphDelegateImpl*>(
mock_delegate()->fake_delegate());
scalable_iph_delegate_impl->SetFakeFeatureStatusProviderForTesting(
&fake_feature_status_provider_);
}
protected:
void AppendCustomCondition(base::FieldTrialParams& params) override {
params[FullyQualified(
TestIphFeature(),
scalable_iph::kCustomConditionPhoneHubOnboardingEligibleParamName)] =
scalable_iph::kCustomConditionPhoneHubOnboardingEligibleValueTrue;
}
ash::phonehub::FakeFeatureStatusProvider fake_feature_status_provider_;
};
class ScalableIphBrowserTestParameterized
: public ScalableIphBrowserTest,
public testing::WithParamInterface<TestEnvironment> {
public:
ScalableIphBrowserTestParameterized() {
// Set `false` as `ScalableIphBrowserTestParameterized` is used to test
// ScalableIph is not eligible cases.
setup_scalable_iph_ = false;
}
void SetUp() override {
SetTestEnvironment(GetParam());
ash::CustomizableTestEnvBrowserTestBase::SetUp();
}
};
class ScalableIphBrowserTestMinor : public ScalableIphBrowserTest {
public:
ScalableIphBrowserTestMinor() {
// `ScalableIphFactoryImpl::GetBrowserContextToUseInternal` uses manta
// service eligibility as a signal to see if a user is a minor or not. Force
// disable manta service to simulate minor user case.
force_disable_manta_service_ = true;
setup_scalable_iph_ = false;
}
};
class MockMessageCenterObserver
: public testing::NiceMock<message_center::MessageCenterObserver> {
public:
// MessageCenterObserver:
MOCK_METHOD(void,
OnNotificationAdded,
(const std::string& notification_id),
(override));
MOCK_METHOD(void,
OnNotificationUpdated,
(const std::string& notification_id),
(override));
};
class ScalableIphBrowserTestNotification : public ScalableIphBrowserTest {
public:
ScalableIphBrowserTestNotification() : has_body_text_(true) {}
protected:
explicit ScalableIphBrowserTestNotification(bool has_body_text)
: has_body_text_(has_body_text) {}
void SetUpOnMainThread() override {
ScalableIphBrowserTest::SetUpOnMainThread();
auto* message_center = message_center::MessageCenter::Get();
scoped_observation_.Observe(message_center);
EXPECT_CALL(mock_, OnNotificationAdded(kTestNotificationId));
mock_delegate()->FakeShowNotification();
}
void AppendUiParams(base::FieldTrialParams& params) override {
AppendFakeUiParamsNotification(params, has_body_text_, TestIphFeature());
}
void TearDownOnMainThread() override {
scoped_observation_.Reset();
ScalableIphBrowserTest::TearDownOnMainThread();
}
protected:
const bool has_body_text_;
private:
// Observe notifications.
MockMessageCenterObserver mock_;
base::ScopedObservation<message_center::MessageCenter,
message_center::MessageCenterObserver>
scoped_observation_{&mock_};
};
class ScalableIphBrowserTestNotificationNoBodyText
: public ScalableIphBrowserTestNotification {
public:
ScalableIphBrowserTestNotificationNoBodyText()
: ScalableIphBrowserTestNotification(/*has_body_text=*/false) {}
};
class ScalableIphBrowserTestBubble : public ScalableIphBrowserTest {
public:
void SetUp() override {
// Set animation duration to zero so the nudge dismisses immediately when
// cancelled or timed out.
ui::ScopedAnimationDurationScaleMode duration_scale(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
ScalableIphBrowserTest::SetUp();
}
protected:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params;
AppendVersionNumber(params);
AppendFakeUiParamsBubble(params);
base::test::FeatureRefAndParams test_config(TestIphFeature(), params);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, test_config}, {});
}
};
class ScalableIphBrowserTestNotificationInvalidConfig
: public ScalableIphBrowserTest {
protected:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params;
AppendVersionNumber(params);
AppendFakeUiParamsNotification(params, /*has_body_text=*/true,
TestIphFeature());
params[FullyQualified(TestIphFeature(),
scalable_iph::kCustomNotificationIdParamName)] = "";
base::test::FeatureRefAndParams test_config(TestIphFeature(), params);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, test_config}, {});
}
};
class ScalableIphBrowserTestBubbleInvalidConfig
: public ScalableIphBrowserTest {
protected:
void InitializeScopedFeatureList() override {
base::FieldTrialParams params;
AppendVersionNumber(params);
AppendFakeUiParamsBubble(params);
params[FullyQualified(TestIphFeature(),
scalable_iph::kCustomBubbleIdParamName)] = "";
base::test::FeatureRefAndParams test_config(TestIphFeature(), params);
base::test::FeatureRefAndParams scalable_iph_feature(
ash::features::kScalableIph, {});
scoped_feature_list_.InitWithFeaturesAndParameters(
{scalable_iph_feature, test_config}, {});
}
};
} // namespace
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestFlagOff, ScalableIphOff) {
ASSERT_FALSE(ash::features::IsScalableIphEnabled());
ASSERT_TRUE(scalable_iph::ScalableIph::IsAnyIphFeatureEnabled());
EXPECT_FALSE(ScalableIphFactory::GetForBrowserContext(browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNoIph, NoIphFeatureFlagOn) {
ASSERT_TRUE(ash::features::IsScalableIphEnabled());
ASSERT_FALSE(scalable_iph::ScalableIph::IsAnyIphFeatureEnabled());
EXPECT_FALSE(ScalableIphFactory::GetForBrowserContext(browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, RecordEvent_FiveMinTick) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, RecordEvent_Unlocked) {
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked));
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kUnlocked);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, InvokeIphByTimer_Notification) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
scalable_iph::ScalableIphDelegate::NotificationParams expected_params;
expected_params.notification_id =
ScalableIphBrowserTestBase::kTestNotificationId;
expected_params.title = ScalableIphBrowserTestBase::kTestNotificationTitle;
expected_params.text = ScalableIphBrowserTestBase::kTestNotificationBodyText;
expected_params.button.text =
ScalableIphBrowserTestBase::kTestNotificationButtonText;
expected_params.button.action.action_type =
scalable_iph::ActionType::kOpenChrome;
expected_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
EXPECT_CALL(*mock_delegate(), ShowNotification(::testing::Eq(expected_params),
::testing::NotNull()))
.WillOnce([](const scalable_iph::ScalableIphDelegate::NotificationParams&
params,
std::unique_ptr<scalable_iph::IphSession> session) {
// Simulate that an IPH gets dismissed.
session.reset();
return true;
});
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, InvokeIphByUnlock_Notification) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
scalable_iph::ScalableIphDelegate::NotificationParams expected_params;
expected_params.notification_id =
ScalableIphBrowserTestBase::kTestNotificationId;
expected_params.title = ScalableIphBrowserTestBase::kTestNotificationTitle;
expected_params.text = ScalableIphBrowserTestBase::kTestNotificationBodyText;
expected_params.button.text =
ScalableIphBrowserTestBase::kTestNotificationButtonText;
expected_params.button.action.action_type =
scalable_iph::ActionType::kOpenChrome;
expected_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
EXPECT_CALL(*mock_delegate(), ShowNotification(::testing::Eq(expected_params),
::testing::NotNull()))
.WillOnce([](const scalable_iph::ScalableIphDelegate::NotificationParams&
params,
std::unique_ptr<scalable_iph::IphSession> session) {
// Simulate that an IPH gets dismissed.
session.reset();
return true;
});
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kUnlocked);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, TimeTickEvent) {
// We test a timer inside ScalableIph service. Make sure that ScalableIph
// service is running.
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
ASSERT_TRUE(scalable_iph);
// Fast forward by 3 mins. The interval of time tick event is 5 mins. No time
// tick event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameFiveMinTick))
.Times(0);
task_runner()->FastForwardBy(base::Minutes(3));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Fast forward by another 3 mins. The total of fast forwarded time is 6 mins.
// A time tick event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameFiveMinTick))
.Times(1);
task_runner()->FastForwardBy(base::Minutes(3));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
ShutdownScalableIph();
// Fast forward by another 6 mins after the shutdown. Shutdown should stop the
// timer and no time tick event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameFiveMinTick))
.Times(0);
task_runner()->FastForwardBy(base::Minutes(6));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, NoTimeTickEventWithLockScreen) {
// We test unlocked event inside ScalableIph service. Make sure that
// ScalableIph service is running.
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
ASSERT_TRUE(scalable_iph);
// Fast forward by 3 mins. The interval of time tick event is 5 mins. No time
// tick event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameFiveMinTick))
.Times(0);
task_runner()->FastForwardBy(base::Minutes(3));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Fast forward by another 3 mins. The total of fast forwarded time is 6 mins.
// But a time tick event will not be observed because device is locked.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameFiveMinTick))
.Times(0);
ash::ScreenLockerTester tester;
tester.Lock();
task_runner()->FastForwardBy(base::Minutes(3));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
// TODO(crbug.com/40924957): Flaky test.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, DISABLED_UnlockedEvent) {
// We test unlocked event inside ScalableIph service. Make sure that
// ScalableIph service is running.
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
ASSERT_TRUE(scalable_iph);
// No Unlocked event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Lock and unlock screen. An Unlocked event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(1);
LockAndUnlockSession();
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Shutdown should stop the observations and no Unlocked event should be
// observed.
ShutdownScalableIph();
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
LockAndUnlockSession();
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, OnSuspendDone) {
// We test unlocked event inside ScalableIph service. Make sure that
// ScalableIph service is running.
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
ASSERT_TRUE(scalable_iph);
// No Unlocked event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Simulate SuspendDone. An Unlocked event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(1);
SendSuspendDone();
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Shutdown should stop the observations and no Unlocked event should be
// observed.
ShutdownScalableIph();
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
SendSuspendDone();
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, OnSuspendDoneWithLockScreen) {
// We test unlocked event inside ScalableIph service. Make sure that
// ScalableIph service is running.
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
ASSERT_TRUE(scalable_iph);
// No Unlocked event should be observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Simulate SuspendDone with lock screen. No Unlocked event should be
// observed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(scalable_iph::kEventNameUnlocked))
.Times(0);
ash::ScreenLockerTester tester;
tester.Lock();
SendSuspendDone();
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
// TODO(crbug.com/40285326): This fails with the field trial testing config.
class ScalableIphBrowserTestNoTestingConfig : public ScalableIphBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
ScalableIphBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch("disable-field-trial-config");
}
};
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNoTestingConfig, AppListShown) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameAppListShown));
ash::AppListTestApi().ShowBubbleAppListAndWait();
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, OpenPersonalizationApp) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameOpenPersonalizationApp));
ash::LaunchSystemWebAppAsync(browser()->profile(),
ash::SystemWebAppType::PERSONALIZATION);
}
// TODO(b/301006258): Migrate to use observer pattern, then enable the test.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, DISABLED_PrintJobCreated) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNamePrintJobCreated));
ash::CupsPrintJobManager* print_job_manager =
ash::CupsPrintJobManagerFactory::GetForBrowserContext(
browser()->profile());
CupsPrintJobManagerWaiter print_job_manager_waiter(print_job_manager,
/*job_id=*/0);
print_job_manager->CreatePrintJob(
"test-printer-id", "title", /*job_id=*/0, /*total_page_number=*/1,
::printing::PrintJob::Source::kPrintPreview, /*source_id=*/"",
ash::printing::proto::PrintSettings());
print_job_manager_waiter.Wait();
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestGame, GameWindowOpened) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameGameWindowOpened));
std::unique_ptr<aura::Window> window = CreateAuraWindow(kTestGameWindowTitle);
window->SetProperty(ash::kAppIDKey,
std::string(extension_misc::kGeForceNowAppId));
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestGameMultiUser,
NoGameWindowOpenedForSecondaryUser) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameGameWindowOpened))
.Times(0);
// Login with secondary user and open a game window with the user.
ash::UserAddingScreen::Get()->Start();
CHECK(GetLoginManagerMixin()->LoginAndWaitForActiveSession(
GetSecondaryUserContext()));
std::unique_ptr<aura::Window> window = CreateAuraWindow(kTestGameWindowTitle);
window->SetProperty(ash::kAppIDKey,
std::string(extension_misc::kGeForceNowAppId));
ash::MultiUserWindowManager* multi_user_window_manager =
MultiUserWindowManagerHelper::GetWindowManager();
CHECK(multi_user_window_manager);
multi_user_window_manager->SetWindowOwner(
window.get(), GetSecondaryUserContext().GetAccountId());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestGameMultiUser,
NoGameWindowOpenedTeleport) {
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameGameWindowOpened))
.Times(0);
// Login with secondary user and open a game window with the user.
ash::UserAddingScreen::Get()->Start();
CHECK(GetLoginManagerMixin()->LoginAndWaitForActiveSession(
GetSecondaryUserContext()));
std::unique_ptr<aura::Window> window = CreateAuraWindow(kTestGameWindowTitle);
ash::MultiUserWindowManager* multi_user_window_manager =
MultiUserWindowManagerHelper::GetWindowManager();
CHECK(multi_user_window_manager);
multi_user_window_manager->SetWindowOwner(
window.get(), GetSecondaryUserContext().GetAccountId());
// Teleport the window before app id is set.
multi_user_window_manager->ShowWindowForUser(
window.get(), GetPrimaryUserContext().GetAccountId());
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
CHECK(user_manager);
CHECK_EQ(user_manager->GetActiveUser()->GetAccountId(),
GetPrimaryUserContext().GetAccountId());
window->SetProperty(ash::kAppIDKey,
std::string(extension_misc::kGeForceNowAppId));
}
// Logging feature is on by default in `ScalableIphBrowserTest`.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTest, Log) {
constexpr char kTestFileNamePattern[] = "*scalable_iph_browsertest.cc*";
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
CHECK(scalable_iph);
// `logging::SetLogMessageHandler` takes a function pointer. Use a static
// variable as a captureless lambda can be converted to a function pointer.
static base::NoDestructor<std::vector<std::string>> captured_logs;
CHECK_EQ(nullptr, logging::GetLogMessageHandler());
logging::SetLogMessageHandler([](int severity, const char* file, int line,
size_t message_start,
const std::string& str) {
captured_logs->push_back(str);
return true;
});
SCALABLE_IPH_LOG(scalable_iph->GetLogger()) << kTestLogMessage;
logging::SetLogMessageHandler(nullptr);
EXPECT_TRUE(base::MatchPattern(scalable_iph->GetLogger()->GenerateLog(),
kTestLogMessagePattern));
EXPECT_TRUE(base::MatchPattern(scalable_iph->GetLogger()->GenerateLog(),
kTestFileNamePattern));
std::string log_output = base::JoinString(*captured_logs, "");
if (DCHECK_IS_ON()) {
EXPECT_TRUE(base::MatchPattern(log_output, kTestLogMessagePattern));
} else {
EXPECT_FALSE(base::MatchPattern(log_output, kTestLogMessagePattern));
}
// Confirms that the debug page is accessible.
content::RenderFrameHost* render_frame_host = ui_test_utils::NavigateToURL(
browser(), GURL(kScalableIphDebugLogTextUrl));
ASSERT_TRUE(render_frame_host);
const network::mojom::URLResponseHead* head =
render_frame_host->GetLastResponseHead();
ASSERT_TRUE(head);
ASSERT_TRUE(head->headers);
EXPECT_EQ(net::HTTP_OK, head->headers->response_code());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestDebugOff, NoLog) {
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
CHECK(scalable_iph);
// `logging::SetLogMessageHandler` takes a function pointer. Use a static
// variable as a captureless lambda can be converted to a function pointer.
static base::NoDestructor<std::vector<std::string>> captured_logs;
CHECK_EQ(nullptr, logging::GetLogMessageHandler());
logging::SetLogMessageHandler([](int severity, const char* file, int line,
size_t message_start,
const std::string& str) {
captured_logs->push_back(str);
return true;
});
SCALABLE_IPH_LOG(scalable_iph->GetLogger()) << kTestLogMessage;
logging::SetLogMessageHandler(nullptr);
EXPECT_TRUE(scalable_iph->GetLogger()->IsLogEmptyForTesting());
std::string log_output = base::JoinString(*captured_logs, "");
EXPECT_FALSE(base::MatchPattern(log_output, kTestLogMessagePattern));
// Confirms that the debug page is not accessible if the flag is off.
content::RenderFrameHost* render_frame_host = ui_test_utils::NavigateToURL(
browser(), GURL(kScalableIphDebugLogTextUrl));
ASSERT_TRUE(render_frame_host);
// Last response head is nullptr if there is no response. See the comment
// of `RenderFrameHost::GetLastResponseHead` for details.
EXPECT_FALSE(render_frame_host->GetLastResponseHead());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestFeatureOffDebugOn,
LogPageAvailable) {
content::RenderFrameHost* render_frame_host = ui_test_utils::NavigateToURL(
browser(), GURL(kScalableIphDebugLogTextUrl));
ASSERT_TRUE(render_frame_host);
const network::mojom::URLResponseHead* head =
render_frame_host->GetLastResponseHead();
ASSERT_TRUE(head);
ASSERT_TRUE(head->headers);
EXPECT_EQ(net::HTTP_OK, head->headers->response_code())
<< "Debug log page is expected to be available even if ScalableIph "
"feature itself is off.";
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestMultipleIphs, OneIphAtATime) {
EnableTestIphFeatures({&TestIphFeature(), &kScalableIphTestTwo});
// Expects that `ShowNotification` gets called exactly once as we expect that
// only a single IPH gets triggered at a time.
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.WillOnce(testing::Return(true));
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestPreinstallApps,
AppListItemActivationWebApp) {
if (!IsGoogleChrome()) {
GTEST_SKIP()
<< "Google Chrome is required for preinstall apps used by this test";
}
// Those constants in `scalable_iph` must be synced with ones in `web_app`.
// Test them in this test case.
EXPECT_EQ(std::string(scalable_iph::kWebAppYouTubeAppId),
std::string(web_app::kYoutubeAppId));
EXPECT_EQ(std::string(scalable_iph::kWebAppGoogleDocsAppId),
std::string(web_app::kGoogleDocsAppId));
AppListClientImpl* app_list_client_impl = AppListClientImpl::GetInstance();
AppListModelUpdater* app_list_model_updater =
test::GetModelUpdater(app_list_client_impl);
AppListItemWaiter app_list_item_waiter(web_app::kYoutubeAppId,
app_list_model_updater);
app_list_item_waiter.Wait();
ash::AppListTestApi().ShowBubbleAppListAndWait();
EXPECT_CALL(
*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameAppListItemActivationYouTube));
app_list_client_impl->ActivateItem(
/*profile_id=*/0, web_app::kYoutubeAppId, /*event_flags=*/0,
ash::AppListLaunchedFrom::kLaunchedFromGrid, /*is_above_the_fold=*/true);
}
// TODO(crbug.com/328713274): Test is flaky.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestPreinstallApps,
DISABLED_ShelfItemActivationWebApp) {
if (!IsGoogleChrome()) {
GTEST_SKIP()
<< "Google Chrome is required for preinstall apps used by this test";
}
apps::AppReadinessWaiter(browser()->profile(),
scalable_iph::kWebAppYouTubeAppId)
.Await();
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameShelfItemActivationYouTube));
ash::Shelf::ActivateShelfItem(ash::ShelfModel::Get()->ItemIndexByAppID(
scalable_iph::kWebAppYouTubeAppId));
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestHelpApp, HelpAppPinnedToShelf) {
if (!IsGoogleChrome()) {
GTEST_SKIP()
<< "Google Chrome is required for preinstall apps used by this test";
}
EXPECT_TRUE(ash::ShelfModel::Get()->IsAppPinned(web_app::kHelpAppId));
}
INSTANTIATE_TEST_SUITE_P(
Perks,
ScalableIphBrowserTestPerksMinecraftRealmsParameterized,
testing::Values(
PerksEnvironment("us",
GURL("https://www.google.com/chromebook/perks/"
"?id=minecraft.realms.2023")),
PerksEnvironment("gb",
GURL("https://www.google.com/chromebook/perks/"
"?id=minecraft.uk.2023")),
PerksEnvironment("ca",
GURL("https://www.google.com/chromebook/perks/"
"?id=minecraft.realms.ca.2023")),
PerksEnvironment("au",
GURL("https://www.google.com/chromebook/perks/"
"?id=minecraft.realms.au.2023"))),
&PerksEnvironment::GenerateTestName);
IN_PROC_BROWSER_TEST_P(ScalableIphBrowserTestPerksMinecraftRealmsParameterized,
Config) {
const std::string country_code = GetParam().country_code();
const GURL expected_perks_url = GetParam().perks_url();
EnableTestIphFeature();
mock_delegate()->FakeShowNotification();
mock_delegate()->FakePerformActionForScalableIph();
OverrideStoredPermanentCountry(country_code);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center::Notification* notification =
message_center->FindVisibleNotificationById(kTestNotificationId);
ASSERT_TRUE(notification);
ASSERT_TRUE(notification->delegate());
ui_test_utils::AllBrowserTabAddedWaiter tab_added_waiter;
notification->delegate()->Click(/*button_index=*/0, /*reply=*/std::nullopt);
content::WebContents* web_contents = tab_added_waiter.Wait();
ASSERT_TRUE(web_contents);
EXPECT_EQ(expected_perks_url, web_contents->GetURL());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestPerksMinecraftRealms,
ActionNotEligible) {
const std::string country_code_jp("jp");
EnableTestIphFeature();
mock_delegate()->FakeShowNotification();
mock_delegate()->FakePerformActionForScalableIph();
OverrideStoredPermanentCountry(country_code_jp);
// If an action is not eligible for a context, IPH is considered to be
// dismissed immediately.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center::Notification* notification =
message_center->FindVisibleNotificationById(kTestNotificationId);
EXPECT_FALSE(notification)
<< scalable_iph::kActionTypeOpenChromebookPerksMinecraftRealms2023
<< " is not supported in " << country_code_jp
<< ". No notification is expected as this config is considered to be "
"invalid.";
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestOobe, SessionState) {
// This is testing post login OOBE screens. In post login OOBE screens:
// - A profile should be loaded.
// - Session state should be OOBE.
// - `ScalableIph` should not have been initialized yet.
ASSERT_EQ(ash::Shell::Get()->session_controller()->GetSessionState(),
session_manager::SessionState::OOBE)
<< "This is an assertion for the test framework. Session state must be "
"OOBE during post-login OOBE";
EXPECT_EQ(nullptr, ScalableIphFactory::GetForBrowserContext(
ProfileManager::GetActiveUserProfile()));
ASSERT_EQ(nullptr, mock_delegate());
// Complete post login OOBE screens. With the completion:
// - Session state will transit from `OOBE` to `LOGGED_IN_NOT_ACTIVE`, and
// then `ACTIVE`.
// - `ScalableIph` should be initialized.
ash::WizardController::default_controller()->SkipPostLoginScreensForTesting();
GetLoginManagerMixin()->WaitForActiveSession();
ASSERT_EQ(ash::Shell::Get()->session_controller()->GetSessionState(),
session_manager::SessionState::ACTIVE)
<< "This is an assertion for the test framework. Session state must be "
"ACTIVE after post-login OOBE";
EXPECT_NE(nullptr, ScalableIphFactory::GetForBrowserContext(
ProfileManager::GetActiveUserProfile()));
SetUpMocks();
EnableTestIphFeature();
ASSERT_TRUE(mock_delegate());
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestVersionNumberNoValue, NoValue) {
EnableTestIphFeature();
// No trigger condition check should happen if it fails to validate a version
// number as the config gets skipped.
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestVersionNumberIncorrect,
Incorrect) {
EnableTestIphFeature();
// No trigger condition check should happen if it fails to validate a version
// number as the config gets skipped.
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestVersionNumberInvalid, Invalid) {
EnableTestIphFeature();
// No trigger condition check should happen if it fails to validate a version
// number as the config gets skipped.
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
// `ScalableIphBrowserTestTriggerEvent` is set up with
// x_CustomConditionTriggerEvent: ScalableIphUnlocked.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestTriggerEvent, TriggerEvent) {
EnableTestIphFeature();
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
// Record an uninterested event. Confirm that this won't trigger an IPH
// trigger condition check.
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(0);
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
// Record an unlocked event, which is an interested event. Confirm that this
// triggers an IPH trigger condition check.
EXPECT_CALL(*mock_tracker(),
ShouldTriggerHelpUI(::testing::Ref(TestIphFeature())))
.Times(1);
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kUnlocked);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNetworkConnection, Online) {
EnableTestIphFeature();
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
AddOnlineNetwork();
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNetworkConnectionOnline,
OnlineFromBeginning) {
EnableTestIphFeature();
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
// We have to trigger a conditions check manually. The trigger condition check
// in `ScalableIph` constructor happens before we set the expectation to the
// delegate mock. We need another event for the next check.
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeZero, Satisfied) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(1));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeZero,
NotSatisfiedAboveThreshold) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(25));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeZero,
NotSatisfiedFutureCreationDate) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() +
base::Hours(1));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeNonZero, Satisfied) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(47));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeNonZero, NotSatisfied) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(49));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeInvalidString,
NotSatisfied) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(1));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestClientAgeInvalidNumber,
NotSatisfied) {
EnableTestIphFeature();
browser()->profile()->SetCreationTimeForTesting(base::Time::Now() -
base::Hours(1));
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestHasSavedPrinters,
ExpectNoSavedPrinters) {
EnableTestIphFeature();
constexpr char kTestPrinterId[] = "test-printer-id";
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
CHECK(scalable_iph);
ash::SyncedPrintersManager* synced_printers_manager =
ash::SyncedPrintersManagerFactory::GetForBrowserContext(
browser()->profile());
CHECK(synced_printers_manager);
// Add a printer. Expect that no IPH gets triggered if there is a saved
// printer.
{
base::RunLoop run_loop;
scalable_iph->SetHasSavedPrintersChangedClosureForTesting(
run_loop.QuitClosure());
synced_printers_manager->UpdateSavedPrinter(
chromeos::Printer(kTestPrinterId));
run_loop.Run();
}
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_delegate());
// Remove the printer and confirm that an IPH gets triggered.
{
base::RunLoop run_loop;
scalable_iph->SetHasSavedPrintersChangedClosureForTesting(
run_loop.QuitClosure());
synced_printers_manager->RemoveSavedPrinter(kTestPrinterId);
run_loop.Run();
}
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_delegate());
}
// Test config is x_CustomConditionPhoneHubOnboardingEligible: True.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestPhoneHubOnboardingEligible,
Eligible) {
EnableTestIphFeature();
// The condition should not be satisfied for `kNotEligibleForFeature`.
fake_feature_status_provider_.SetStatus(
ash::phonehub::FeatureStatus::kNotEligibleForFeature);
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_delegate());
// The condition should be satisfied for `kEligiblePhoneButNotSetUp`.
fake_feature_status_provider_.SetStatus(
ash::phonehub::FeatureStatus::kEligiblePhoneButNotSetUp);
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_delegate());
// The condition should be satisfied for `kDisabled`. See the comment of
// `FeatureStatus::kDisabled` about the meaning of the enum value.
fake_feature_status_provider_.SetStatus(
ash::phonehub::FeatureStatus::kDisabled);
EXPECT_CALL(*mock_delegate(),
ShowNotification(::testing::_, ::testing::NotNull()))
.Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
testing::Mock::VerifyAndClearExpectations(mock_delegate());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNotification, ShowNotification) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is not performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
auto* message_center = message_center::MessageCenter::Get();
auto* notification =
message_center->FindVisibleNotificationById(kTestNotificationId);
EXPECT_TRUE(notification);
message_center->RemoveNotification(kTestNotificationId,
/*by_user=*/false);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNotification,
ClickNotificationButton) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent));
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
auto* message_center = message_center::MessageCenter::Get();
auto* notification =
message_center->FindVisibleNotificationById(kTestNotificationId);
EXPECT_TRUE(notification);
EXPECT_TRUE(notification->delegate());
// `PerformActionForScalableIph` should be called with the corresponding CTA
// action_type when a notification is clicked.
EXPECT_CALL(*mock_delegate(), PerformActionForScalableIph(::testing::Eq(
scalable_iph::ActionType::kOpenChrome)));
notification->delegate()->Click(/*button_index=*/0, /*reply=*/std::nullopt);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
// Test that a scalable_iph NotificationParam with an empty body text can create
// a notification, i.e., make sure that it's accepted input.
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNotificationNoBodyText,
ShowNotification) {
EnableTestIphFeature();
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
message_center::Notification* notification =
message_center->FindVisibleNotificationById(kTestNotificationId);
EXPECT_TRUE(notification);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubble, InvokeIphByTimer_Bubble) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
scalable_iph::ScalableIphDelegate::BubbleParams expected_params;
expected_params.bubble_id = ScalableIphBrowserTestBase::kTestBubbleId;
expected_params.title = ScalableIphBrowserTestBase::kTestBubbleTitle;
expected_params.text = ScalableIphBrowserTestBase::kTestBubbleText;
expected_params.button.text =
ScalableIphBrowserTestBase::kTestBubbleButtonText;
expected_params.button.action.action_type =
scalable_iph::ActionType::kOpenGoogleDocs;
expected_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
expected_params.icon =
scalable_iph::ScalableIphDelegate::BubbleIcon::kGoogleDocsIcon;
EXPECT_CALL(*mock_delegate(),
ShowBubble(::testing::Eq(expected_params), ::testing::NotNull()))
.WillOnce(
[](const scalable_iph::ScalableIphDelegate::BubbleParams& params,
std::unique_ptr<scalable_iph::IphSession> session) {
// Simulate that an IPH gets dismissed.
session.reset();
return true;
});
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kFiveMinTick);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubble, InvokeIphByUnlock_Bubble) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
scalable_iph::ScalableIphDelegate::BubbleParams expected_params;
expected_params.bubble_id = ScalableIphBrowserTestBase::kTestBubbleId;
expected_params.title = ScalableIphBrowserTestBase::kTestBubbleTitle;
expected_params.text = ScalableIphBrowserTestBase::kTestBubbleText;
expected_params.button.text =
ScalableIphBrowserTestBase::kTestBubbleButtonText;
expected_params.button.action.action_type =
scalable_iph::ActionType::kOpenGoogleDocs;
expected_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
expected_params.icon =
scalable_iph::ScalableIphDelegate::BubbleIcon::kGoogleDocsIcon;
EXPECT_CALL(*mock_delegate(),
ShowBubble(::testing::Eq(expected_params), ::testing::NotNull()))
.WillOnce(
[](const scalable_iph::ScalableIphDelegate::BubbleParams& params,
std::unique_ptr<scalable_iph::IphSession> session) {
// Simulate that an IPH gets dismissed.
session.reset();
return true;
});
scalable_iph::ScalableIph* scalable_iph =
ScalableIphFactory::GetForBrowserContext(browser()->profile());
scalable_iph->RecordEvent(scalable_iph::ScalableIph::Event::kUnlocked);
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubble, ShowBubbleAndDismiss) {
EnableTestIphFeature();
mock_delegate()->FakeShowBubble();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is not performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(0);
{
// A timer used for a nudge dismiss is created via
// `AnchoredNudgeManager::Show` call. Call the method in the scoped context
// of `TestMockTimeTaskRunner` as we can fast-forward it below.
base::TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner());
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
}
ash::AnchoredNudgeManager* anchored_nudge_manager =
ash::AnchoredNudgeManager::Get();
CHECK(anchored_nudge_manager);
EXPECT_TRUE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
// Fast forward nudge medium duration + 1 second.
task_runner()->FastForwardBy(
ash::AnchoredNudgeManagerImpl::kNudgeMediumDuration + base::Seconds(1));
EXPECT_FALSE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubble, RemoveBubble) {
EnableTestIphFeature();
mock_delegate()->FakeShowBubble();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is not performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(0);
EXPECT_CALL(*mock_delegate(), PerformActionForScalableIph(::testing::Eq(
scalable_iph::ActionType::kOpenGoogleDocs)))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
ash::AnchoredNudgeManager* anchored_nudge_manager =
ash::AnchoredNudgeManager::Get();
CHECK(anchored_nudge_manager);
EXPECT_TRUE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
ash::AnchoredNudgeManager::Get()->Cancel(kTestBubbleId);
EXPECT_FALSE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubble, ClickBubble) {
EnableTestIphFeature();
mock_delegate()->FakeShowBubble();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(), Dismissed(::testing::Ref(TestIphFeature())));
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(1);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
ash::AnchoredNudgeManager* anchored_nudge_manager =
ash::AnchoredNudgeManager::Get();
CHECK(anchored_nudge_manager);
EXPECT_TRUE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
// `PerformActionForScalableIph` should be called with the corresponding CTA
// action_type when a bubble is clicked.
EXPECT_CALL(*mock_delegate(),
PerformActionForScalableIph(
::testing::Eq(scalable_iph::ActionType::kOpenGoogleDocs)));
views::View* nudge_button =
ash::Shell::Get()->anchored_nudge_manager()->GetNudgePrimaryButtonForTest(
kTestBubbleId);
ui::test::EventGenerator event_generator(ash::Shell::GetPrimaryRootWindow());
event_generator.MoveMouseTo(nudge_button->GetBoundsInScreen().CenterPoint());
event_generator.ClickLeftButton();
EXPECT_FALSE(anchored_nudge_manager->IsNudgeShown(kTestBubbleId));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestNotificationInvalidConfig,
NotShowNotification) {
EnableTestIphFeature();
// Tracker::Dismissed must be called when an IPH gets dismissed.
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is not performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(0);
// Simulate an invalid config (i.e. missing notification_id).
scalable_iph::ScalableIphDelegate::NotificationParams invalid_params;
invalid_params.notification_id = "";
invalid_params.title = ScalableIphBrowserTestBase::kTestNotificationTitle;
invalid_params.text = ScalableIphBrowserTestBase::kTestNotificationBodyText;
invalid_params.button.text =
ScalableIphBrowserTestBase::kTestNotificationButtonText;
invalid_params.button.action.action_type =
scalable_iph::ActionType::kOpenChrome;
invalid_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
// When the config params are invalid and/or not parsable, the notification
// should not be shown.
EXPECT_CALL(*mock_delegate(), ShowNotification(::testing::Eq(invalid_params),
::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
// Check that a notification is not shown.
auto* message_center = message_center::MessageCenter::Get();
auto* notification = message_center->FindVisibleNotificationById("");
EXPECT_FALSE(notification);
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestBubbleInvalidConfig,
NotShowBubble) {
EnableTestIphFeature();
mock_delegate()->FakeShowBubble();
EXPECT_CALL(*mock_tracker(),
NotifyEvent(scalable_iph::kEventNameFiveMinTick));
// The action is not performed.
EXPECT_CALL(*mock_tracker(), NotifyEvent(kTestButtonActionEvent)).Times(0);
// Simulate an invalid config (i.e. missing bubble_id).
scalable_iph::ScalableIphDelegate::BubbleParams invalid_params;
invalid_params.bubble_id = "";
invalid_params.title = ScalableIphBrowserTestBase::kTestBubbleTitle;
invalid_params.text = ScalableIphBrowserTestBase::kTestBubbleText;
invalid_params.button.text =
ScalableIphBrowserTestBase::kTestBubbleButtonText;
invalid_params.button.action.action_type =
scalable_iph::ActionType::kOpenGoogleDocs;
invalid_params.button.action.iph_event_name =
ScalableIphBrowserTestBase::kTestButtonActionEvent;
invalid_params.icon =
scalable_iph::ScalableIphDelegate::BubbleIcon::kGoogleDocsIcon;
// When the config params are invalid and/or not parsable, the notification
// should not be shown.
EXPECT_CALL(*mock_delegate(),
ShowBubble(::testing::Eq(invalid_params), ::testing::NotNull()))
.Times(0);
TriggerConditionsCheckWithAFakeEvent(
scalable_iph::ScalableIph::Event::kFiveMinTick);
ash::AnchoredNudgeManager* anchored_nudge_manager =
ash::AnchoredNudgeManager::Get();
CHECK(anchored_nudge_manager);
EXPECT_FALSE(anchored_nudge_manager->IsNudgeShown(""));
testing::Mock::VerifyAndClearExpectations(mock_tracker());
}
INSTANTIATE_TEST_SUITE_P(
NoScalableIph,
ScalableIphBrowserTestParameterized,
testing::Values(
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED,
UserSessionType::kManaged),
// A test case where a regular profile on a managed device.
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED,
UserSessionType::kRegular),
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kGuest),
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kChild),
// A test case where a child profile is an owner of a device.
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kChildOwner),
// A Test case where a managed account is an owner of an un-enrolled
// device.
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kManaged),
// A test case where a regular profile is not an owner profile.
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kRegularNonOwner)),
&TestEnvironment::GenerateTestName);
IN_PROC_BROWSER_TEST_P(ScalableIphBrowserTestParameterized,
ScalableIphNotAvailable) {
EXPECT_EQ(nullptr,
ScalableIphFactory::GetForBrowserContext(browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(ScalableIphBrowserTestMinor, ScalableIphNotAvailable) {
ASSERT_EQ(ash::CustomizableTestEnvBrowserTestBase::UserSessionType::kRegular,
test_environment().user_session_type())
<< "This test uses kRegular user session type without "
"can_use_manta_service=true capability to simulate minor account.";
EXPECT_EQ(nullptr,
ScalableIphFactory::GetForBrowserContext(browser()->profile()));
}
INSTANTIATE_TEST_SUITE_P(
NoHelpAppPin,
ScalableIphBrowserTestHelpAppParameterized,
testing::Values(
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kGuest),
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kManaged),
TestEnvironment(
ash::DeviceStateMixin::State::OOBE_COMPLETED_CONSUMER_OWNED,
UserSessionType::kRegularNonOwner)),
&TestEnvironment::GenerateTestName);
IN_PROC_BROWSER_TEST_P(ScalableIphBrowserTestHelpAppParameterized,
HelpAppNotPinnedToShelf) {
EXPECT_FALSE(ash::ShelfModel::Get()->IsAppPinned(web_app::kHelpAppId));
}