blob: 818d98ce84ac8337bb0f302c809057ad0e797339 [file] [log] [blame]
// Copyright 2021 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/test/base/in_process_browser_test.h"
#include "base/callback_list.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "chrome/browser/cart/cart_db_content.pb.h"
#include "chrome/browser/cart/cart_service.h"
#include "chrome/browser/commerce/commerce_feature_list.h"
#include "chrome/browser/persisted_state_db/profile_proto_db.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/common/pref_names.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/prefs/pref_service.h"
#include "components/search/ntp_features.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/test/browser_test.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
namespace {
std::string BuildPartnerMerchantPattern(
std::vector<std::string>& parter_merchant_list) {
std::string pattern = "(";
pattern += base::JoinString(parter_merchant_list, "|");
pattern += ")";
return pattern;
}
std::unique_ptr<net::test_server::HttpResponse> BasicResponse(
bool return_empty_response,
const net::test_server::HttpRequest& request) {
if (request.relative_url == "/coupons/fl_codeless_discounts.json" &&
!return_empty_response) {
return nullptr;
}
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content("{}");
response->set_content_type("application/json; charset=UTF-8");
return response;
}
cart_db::ChromeCartContentProto BuildCartProto(const char* domain,
const char* merchant_url) {
cart_db::ChromeCartContentProto proto;
proto.set_key(domain);
proto.set_merchant_cart_url(merchant_url);
proto.set_timestamp(base::Time::Now().ToDoubleT());
return proto;
}
cart_db::ChromeCartContentProto BuildCartProtoWithCoupon(
const char* domain,
const char* merchant_url) {
cart_db::ChromeCartContentProto proto = BuildCartProto(domain, merchant_url);
proto.mutable_discount_info()->set_has_coupons(true);
return proto;
}
using ShoppingCarts =
std::vector<ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>;
} // namespace
class FetchDiscountWorkerBrowserTest : public InProcessBrowserTest {
public:
void SetUpInProcessBrowserTestFixture() override {
create_services_subscription_ =
BrowserContextDependencyManager::GetInstance()
->RegisterCreateServicesCallbackForTesting(
base::BindRepeating(&FetchDiscountWorkerBrowserTest::
OnWillCreateBrowserContextServices,
base::Unretained(this)));
}
void SetUpOnMainThread() override {
Profile* profile = browser()->profile();
identity_test_environment_adaptor_ =
std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile);
service_ = CartServiceFactory::GetForProfile(profile);
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->ServeFilesFromSourceDirectory(
"chrome/test/data/cart");
embedded_test_server()->RegisterDefaultHandler(
base::BindRepeating(&BasicResponse, false /*return_empty_response*/));
// Simulate discount consent is accepted.
profile->GetPrefs()->SetBoolean(prefs::kCartDiscountEnabled, true);
SignIn();
}
void TearDownOnMainThread() override {
identity_test_environment_adaptor_.reset();
}
void CheckFLCodelessDiscounts(base::OnceClosure closure,
bool contained_coupon,
std::string expected_discount_text,
bool success,
ShoppingCarts found) {
EXPECT_TRUE(success);
EXPECT_EQ(1U, found.size());
EXPECT_TRUE(found[0].second.has_discount_info());
EXPECT_EQ(contained_coupon, found[0].second.discount_info().has_coupons());
EXPECT_EQ(expected_discount_text,
found[0].second.discount_info().discount_text());
std::move(closure).Run();
}
protected:
void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
IdentityTestEnvironmentProfileAdaptor::
SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
}
void SignIn() {
identity_test_environment_adaptor_->identity_test_env()
->MakePrimaryAccountAvailable("user@gmail.com",
signin::ConsentLevel::kSync);
identity_test_environment_adaptor_->identity_test_env()
->SetAutomaticIssueOfAccessTokens(true);
}
void CreateCart(const std::string& domain,
cart_db::ChromeCartContentProto cart_proto) {
CartDB* cart_db = service_->GetDB();
base::RunLoop run_loop;
cart_db->AddCart(
domain, cart_proto,
base::BindOnce(&FetchDiscountWorkerBrowserTest::OnCartAdded,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void OnCartAdded(base::OnceClosure closure, bool success) {
EXPECT_TRUE(success);
std::move(closure).Run();
}
void StartGettingDiscount() { service_->StartGettingDiscount(); }
void waitForDiscounts(const std::string& cart_domain) {
satisfied_ = false;
while (true) {
base::RunLoop().RunUntilIdle();
base::RunLoop run_loop;
service_->LoadCart(
cart_domain,
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckLastFetchTime,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
if (satisfied_)
break;
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
}
}
void CheckLastFetchTime(base::OnceClosure closure,
bool success,
ShoppingCarts found) {
EXPECT_TRUE(success);
EXPECT_EQ(1U, found.size());
if (found[0].second.has_discount_info()) {
if (found[0].second.discount_info().last_fetched_timestamp() != 0) {
satisfied_ = true;
} else {
VLOG(2) << "last_fetched_timestamp not set";
}
} else {
VLOG(2) << "Not contain discount_info";
}
std::move(closure).Run();
}
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
identity_test_environment_adaptor_;
base::CallbackListSubscription create_services_subscription_;
CartService* service_;
bool satisfied_;
};
class FetchFLCodelessDiscountWorkerBrowserTest
: public FetchDiscountWorkerBrowserTest {
public:
FetchFLCodelessDiscountWorkerBrowserTest() {
parter_merchant_list_.push_back("merchant0.com");
parter_merchant_list_.push_back("merchant1.com");
parter_merchant_list_.push_back("merchant2.com");
}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
std::vector<base::test::ScopedFeatureList::FeatureAndParams>
enabled_features;
base::FieldTrialParams cart_params, coupon_params;
cart_params[ntp_features::kNtpChromeCartModuleAbandonedCartDiscountParam] =
"true";
cart_params["CartDiscountFetcherEndpointParam"] =
embedded_test_server()
->GetURL("/coupons/fl_codeless_discounts.json")
.spec();
enabled_features.emplace_back(ntp_features::kNtpChromeCartModule,
cart_params);
coupon_params["coupon-partner-merchant-pattern"] =
BuildPartnerMerchantPattern(parter_merchant_list_);
enabled_features.emplace_back(commerce::kRetailCoupons, coupon_params);
scoped_feature_list_.InitWithFeaturesAndParameters(
enabled_features,
/*disabled_features*/ {
optimization_guide::features::kOptimizationHints});
}
protected:
std::vector<std::string> parter_merchant_list_;
};
IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
SimplePercentOffTest) {
embedded_test_server()->StartAcceptingConnections();
CreateCart("merchant1.com",
BuildCartProto("merchant1.com", "https://www.merchant1.com/cart"));
StartGettingDiscount();
waitForDiscounts("merchant1.com");
// Verify discounts.
base::RunLoop run_loop;
std::string expected_discount_text = "10% off";
service_->LoadCart(
"merchant1.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop.QuitClosure(), true,
expected_discount_text));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
SimpleDollarOffTest) {
embedded_test_server()->StartAcceptingConnections();
CreateCart("merchant2.com",
BuildCartProto("merchant2.com", "https://www.merchant2.com/cart"));
StartGettingDiscount();
waitForDiscounts("merchant2.com");
// Verify discounts.
base::RunLoop run_loop;
std::string expected_discount_text = "$2 off";
service_->LoadCart(
"merchant2.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop.QuitClosure(), true,
expected_discount_text));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
NoDiscountForThisMerchantTest) {
embedded_test_server()->StartAcceptingConnections();
CreateCart("merchant0.com",
BuildCartProto("merchant0.com", "https://www.merchant0.com/cart"));
StartGettingDiscount();
waitForDiscounts("merchant0.com");
// Verify discounts.
base::RunLoop run_loop;
service_->LoadCart(
"merchant0.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop.QuitClosure(), false,
""));
run_loop.Run();
}
IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
TwoCartsOneWithDiscountOneWithoutDiscount) {
embedded_test_server()->StartAcceptingConnections();
CreateCart("merchant0.com",
BuildCartProto("merchant0.com", "https://www.merchant0.com/cart"));
CreateCart("merchant1.com",
BuildCartProto("merchant1.com", "https://www.merchant1.com/cart"));
StartGettingDiscount();
waitForDiscounts("merchant0.com");
waitForDiscounts("merchant1.com");
// Verify discounts.
base::RunLoop run_loop[2];
service_->LoadCart(
"merchant0.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop[0].QuitClosure(), false,
""));
run_loop[0].Run();
service_->LoadCart(
"merchant1.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop[1].QuitClosure(), true,
"10% off" /*expected_discount_text*/));
run_loop[1].Run();
}
IN_PROC_BROWSER_TEST_F(FetchFLCodelessDiscountWorkerBrowserTest,
CartDiscountBecomeUnavailable) {
CreateCart("merchant1.com",
BuildCartProtoWithCoupon("merchant1.com",
"https://www.merchant1.com/cart"));
// Config server to return empty response.
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&BasicResponse, true /*return_empty_response*/));
embedded_test_server()->StartAcceptingConnections();
StartGettingDiscount();
waitForDiscounts("merchant1.com");
// Verify cart has no coupon discount.
base::RunLoop run_loop;
service_->LoadCart(
"merchant1.com",
base::BindOnce(&FetchDiscountWorkerBrowserTest::CheckFLCodelessDiscounts,
base::Unretained(this), run_loop.QuitClosure(), false,
""));
run_loop.Run();
}