blob: ef3866eda139f240eac9437ae41ec86006e42bef [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/new_tab_page/promos/promo_service.h"
#include <utility>
#include <vector>
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "chrome/browser/new_tab_page/promos/promo_data.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/search/ntp_features.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_task_environment.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
using testing::Eq;
using testing::StartsWith;
class PromoServiceTest : public testing::Test {
public:
PromoServiceTest()
: task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
test_shared_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {}
void SetUp() override {
testing::Test::SetUp();
service_ =
std::make_unique<PromoService>(test_shared_loader_factory_, &profile_);
}
void SetUpResponseWithData(const GURL& load_url,
const std::string& response) {
test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
[&](const network::ResourceRequest& request) {}));
test_url_loader_factory_.AddResponse(load_url.spec(), response);
}
void SetUpResponseWithNetworkError(const GURL& load_url) {
test_url_loader_factory_.AddResponse(
load_url, network::mojom::URLResponseHead::New(), std::string(),
network::URLLoaderCompletionStatus(net::HTTP_NOT_FOUND));
}
PromoService* service() { return service_.get(); }
PrefService* prefs() { return profile_.GetPrefs(); }
private:
// Required to run tests from UI and threads.
content::BrowserTaskEnvironment task_environment_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
std::unique_ptr<PromoService> service_;
TestingProfile profile_;
};
TEST_F(PromoServiceTest, PromoDataNetworkError) {
SetUpResponseWithNetworkError(service()->GetLoadURLForTesting());
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(service()->promo_data(), absl::nullopt);
EXPECT_EQ(service()->promo_status(), PromoService::Status::TRANSIENT_ERROR);
}
TEST_F(PromoServiceTest, BadPromoResponse) {
SetUpResponseWithData(service()->GetLoadURLForTesting(),
"{\"update\":{\"promotions\":{}}}");
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(service()->promo_data(), absl::nullopt);
EXPECT_EQ(service()->promo_status(), PromoService::Status::FATAL_ERROR);
}
TEST_F(PromoServiceTest, PromoResponseMissingData) {
SetUpResponseWithData(service()->GetLoadURLForTesting(),
"{\"update\":{\"promos\":{}}}");
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(service()->promo_data(), PromoData());
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITHOUT_PROMO);
}
TEST_F(PromoServiceTest, GoodPromoResponse) {
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
promo.promo_log_url = GURL("https://www.google.com/log_url?id=42");
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
}
TEST_F(PromoServiceTest, GoodPromoResponseCanDismiss) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
promo.promo_log_url = GURL("https://www.google.com/log_url?id=42");
promo.promo_id = "42";
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
}
TEST_F(PromoServiceTest, GoodPromoResponseNoIdField) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
promo.promo_log_url = GURL("https://www.google.com/log_url?id=42");
promo.promo_id = "42";
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
}
TEST_F(PromoServiceTest, GoodPromoResponseNoIdFieldNorLogUrl) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
}
TEST_F(PromoServiceTest, GoodPromoWithBlockedID) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
{
DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
base::Time recent = base::Time::Now() - base::Hours(2);
update->SetDoubleKey("42", recent.ToDeltaSinceWindowsEpoch().InSecondsF());
}
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(service()->promo_data(), PromoData());
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_BUT_BLOCKED);
}
TEST_F(PromoServiceTest, BlocklistPromo) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
ASSERT_EQ(service()->promo_data(), absl::nullopt);
service()->Refresh();
base::RunLoop().RunUntilIdle();
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
promo.promo_log_url = GURL("https://www.google.com/log_url?id=42");
promo.promo_id = "42";
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
ASSERT_EQ(0u, prefs()->GetDictionary(prefs::kNtpPromoBlocklist)->DictSize());
service()->BlocklistPromo("42");
EXPECT_EQ(service()->promo_data(), PromoData());
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_BUT_BLOCKED);
const auto* blocklist = prefs()->GetDictionary(prefs::kNtpPromoBlocklist);
ASSERT_EQ(1u, blocklist->DictSize());
ASSERT_TRUE(blocklist->FindKey("42"));
}
TEST_F(PromoServiceTest, BlocklistExpiration) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
{
DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
ASSERT_EQ(0u, update->DictSize());
base::Time past = base::Time::Now() - base::Days(365);
update->SetDoubleKey("42", past.ToDeltaSinceWindowsEpoch().InSecondsF());
}
ASSERT_EQ(1u, prefs()->GetDictionary(prefs::kNtpPromoBlocklist)->DictSize());
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
service()->Refresh();
base::RunLoop().RunUntilIdle();
// The year-old entry of {promo_id: "42", time: <1y ago>} should be gone.
ASSERT_EQ(0u, prefs()->GetDictionary(prefs::kNtpPromoBlocklist)->DictSize());
// The promo should've still been shown, as expiration should take precedence.
PromoData promo;
promo.promo_html = "<style></style><div><script></script></div>";
promo.promo_log_url = GURL("https://www.google.com/log_url?id=42");
promo.promo_id = "42";
EXPECT_EQ(service()->promo_data(), promo);
EXPECT_EQ(service()->promo_status(), PromoService::Status::OK_WITH_PROMO);
}
TEST_F(PromoServiceTest, BlocklistWrongExpiryType) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(ntp_features::kDismissPromos);
{
DictionaryPrefUpdate update(prefs(), prefs::kNtpPromoBlocklist);
ASSERT_EQ(0u, update->DictSize());
update->SetDoubleKey("42", 5);
update->SetStringKey("84", "wrong type");
}
ASSERT_GT(prefs()->GetDictionary(prefs::kNtpPromoBlocklist)->DictSize(), 0u);
std::string response_string =
"{\"update\":{\"promos\":{\"middle\":\"<style></style><div><script></"
"script></div>\", \"log_url\":\"/log_url?id=42\", \"id\": \"42\"}}}";
SetUpResponseWithData(service()->GetLoadURLForTesting(), response_string);
service()->Refresh();
base::RunLoop().RunUntilIdle();
// All the invalid formats should've been removed from the pref.
ASSERT_EQ(0u, prefs()->GetDictionary(prefs::kNtpPromoBlocklist)->DictSize());
}