// 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.
#import "ios/chrome/browser/ui/promos_manager/promos_manager_coordinator.h"
#import <Foundation/Foundation.h>
#import "base/apple/foundation_util.h"
#import "base/ios/ios_util.h"
#import "base/test/scoped_feature_list.h"
#import "components/prefs/pref_registry_simple.h"
#import "components/prefs/testing_pref_service.h"
#import "components/sync_preferences/testing_pref_service_syncable.h"
#import "ios/chrome/app/application_delegate/app_state+Testing.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/app/application_delegate/fake_startup_information.h"
#import "ios/chrome/browser/promos_manager/model/features.h"
#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
#import "ios/chrome/browser/shared/coordinator/scene/test/fake_scene_state.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h"
#import "ios/chrome/browser/shared/public/commands/docking_promo_commands.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/signin/model/fake_authentication_service_delegate.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/browser/ui/promos_manager/bannered_promo_view_provider.h"
#import "ios/chrome/browser/ui/promos_manager/promos_manager_coordinator+Testing.h"
#import "ios/chrome/browser/ui/promos_manager/standard_promo_action_handler.h"
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h"
#import "ios/chrome/common/ui/promo_style/promo_style_view_controller.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/chrome/test/testing_application_context.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/gmock/include/gmock/gmock.h"
#import "testing/gtest/include/gtest/gtest.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
namespace {
class PromosManagerCoordinatorTest : public PlatformTest {
public:
void SetUp() override {
auto testing_prefs =
std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
RegisterBrowserStatePrefs(testing_prefs->registry());
TestChromeBrowserState::Builder builder;
builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
SyncServiceFactory::GetDefaultFactory());
builder.SetPrefService(std::move(testing_prefs));
builder.AddTestingFactory(
AuthenticationServiceFactory::GetInstance(),
AuthenticationServiceFactory::GetDefaultFactory());
browser_state_ = std::move(builder).Build();
AuthenticationServiceFactory::CreateAndInitializeForBrowserState(
browser_state_.get(),
std::make_unique<FakeAuthenticationServiceDelegate>());
view_controller_ = [[UIViewController alloc] init];
[scoped_key_window_.Get() setRootViewController:view_controller_];
TestingApplicationContext::GetGlobal()->SetLastShutdownClean(true);
startup_information_ = [[FakeStartupInformation alloc] init];
[startup_information_ setIsColdStart:YES];
AppState* app_state = OCMClassMock([AppState class]);
OCMStub([(AppState*)app_state initStage]).andReturn(InitStageFinal);
scene_state_ =
[[FakeSceneState alloc] initWithAppState:app_state
browserState:browser_state_.get()];
scene_state_.scene = static_cast<UIWindowScene*>(
[[[UIApplication sharedApplication] connectedScenes] anyObject]);
browser_ =
std::make_unique<TestBrowser>(browser_state_.get(), scene_state_);
}
// Initializes a new `PromosManagerCoordinator` for testing.
void CreatePromosManagerCoordinator() {
coordinator_ = [[PromosManagerCoordinator alloc]
initWithBaseViewController:view_controller_
browser:browser_.get()
credentialProviderPromoHandler:OCMStrictProtocolMock(@protocol(
CredentialProviderPromoCommands))
dockingPromoHandler:OCMStrictProtocolMock(
@protocol(DockingPromoCommands))];
}
// Forces the test promo for display.
void ForceTestPromo() {
[[NSUserDefaults standardUserDefaults]
setObject:@"promos_manager::Promo::Test"
forKey:@"NextPromoForDisplayOverride"];
}
// Sets up the UI to be ready for promo display.
void SetupUIForPromoDisplay() {
browser_.get()->GetSceneState().activationLevel =
SceneActivationLevelForegroundActive;
}
protected:
IOSChromeScopedTestingLocalState scoped_testing_local_state_;
base::test::ScopedFeatureList scoped_feature_list_;
PromosManagerCoordinator* coordinator_;
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
std::unique_ptr<TestBrowser> browser_;
ScopedKeyWindow scoped_key_window_;
UIViewController* view_controller_;
FakeStartupInformation* startup_information_;
FakeSceneState* scene_state_;
};
} // namespace
// Tests a provider's standardPromoDismissAction is called when a
// viewController's dismiss button is pressed.
TEST_F(PromosManagerCoordinatorTest,
ViewControllerDismissesUsingDismissButton) {
CreatePromosManagerCoordinator();
id provider = OCMProtocolMock(@protocol(StandardPromoViewProvider));
coordinator_.provider = provider;
OCMExpect([provider standardPromoDismissAction]);
[coordinator_ confirmationAlertDismissAction];
[provider verify];
}
// Tests a banneredProvider's standardPromoDismissAction is called when a
// banneredViewController's dismiss button is pressed.
TEST_F(PromosManagerCoordinatorTest,
BanneredViewControllerDismissesUsingSecondaryButton) {
CreatePromosManagerCoordinator();
id banneredProvider = OCMProtocolMock(@protocol(BanneredPromoViewProvider));
coordinator_.banneredProvider = banneredProvider;
OCMExpect([banneredProvider standardPromoDismissAction]);
[coordinator_ confirmationAlertDismissAction];
[banneredProvider verify];
}
// Tests standardPromoDismissSwipe is called when a viewController is
// dismissed via swipe.
TEST_F(PromosManagerCoordinatorTest, ViewControllerDismissesViaSwipe) {
CreatePromosManagerCoordinator();
id provider = OCMProtocolMock(@protocol(StandardPromoViewProvider));
coordinator_.provider = provider;
OCMExpect([provider standardPromoDismissSwipe]);
[coordinator_ presentationControllerDidDismiss:nil];
[provider verify];
}
// Tests standardPromoDismissSwipe is called when a banneredViewController is
// dismissed via swipe.
TEST_F(PromosManagerCoordinatorTest, BanneredViewControllerDismissesViaSwipe) {
CreatePromosManagerCoordinator();
id banneredProvider = OCMProtocolMock(@protocol(BanneredPromoViewProvider));
coordinator_.banneredProvider = banneredProvider;
OCMExpect([banneredProvider standardPromoDismissSwipe]);
[coordinator_ presentationControllerDidDismiss:nil];
[banneredProvider verify];
}
// Tests that promo will be displayed if UI is available.
TEST_F(PromosManagerCoordinatorTest, DisplayPromoCallbackTest) {
// Prepare UI for promo display.
SetupUIForPromoDisplay();
CreatePromosManagerCoordinator();
id mockCoordinator = OCMPartialMock(coordinator_);
// Force test promos so there at least one promo to display.
ForceTestPromo();
// Check that test promo will be displayed.
PromoDisplayData promoDisplayData = PromoDisplayData{
.promo = promos_manager::Promo::Test, .was_forced = true};
OCMExpect([mockCoordinator displayPromo:promoDisplayData]);
[mockCoordinator displayPromoCallback:true];
[mockCoordinator verify];
}
// Tests that promo will not be displayed if UI is not available.
TEST_F(PromosManagerCoordinatorTest, DisplayPromoCallbackUINotAvailableTest) {
// Prepare UI for promo display.
SetupUIForPromoDisplay();
CreatePromosManagerCoordinator();
id mockCoordinator = OCMPartialMock(coordinator_);
// Force test promos so there at least one promo to display.
ForceTestPromo();
// Check that test promo will not be displayed.
PromoDisplayData promoDisplayData = PromoDisplayData{
.promo = promos_manager::Promo::Test, .was_forced = true};
OCMReject([mockCoordinator displayPromo:promoDisplayData]);
// Set UI not available for promo display before calling
// ```displayPromoCallback```
browser_.get()->GetSceneState().activationLevel =
SceneActivationLevelBackground;
[mockCoordinator displayPromoCallback:true];
[mockCoordinator verify];
}
// TODO(crbug.com/40241101): Add unit tests for promoWasDisplayed being
// called when promo is displayed.