blob: 069053433b38a14b2db53519ecdfedfd5216325a [file] [log] [blame]
// 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.
#include "base/i18n/time_formatting.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browsing_topics/browsing_topics_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/webui/browsing_topics/browsing_topics_internals_page_handler.h"
#include "chrome/browser/ui/webui/browsing_topics/browsing_topics_internals_ui.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_topics/browsing_topics_service.h"
#include "components/browsing_topics/epoch_topics.h"
#include "components/browsing_topics/test_util.h"
#include "components/optimization_guide/core/test_model_info_builder.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_topics_test_util.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/base/l10n/l10n_util.h"
namespace browsing_topics {
namespace {
const char kBrowsingTopicsInternalsUrl[] = "chrome://topics-internals/";
const char kBrowsingTopicsInternalsConsentInfoUrl[] =
"chrome://topics-internals/#consent-info";
class FixedBrowsingTopicsService
: public browsing_topics::BrowsingTopicsService {
public:
FixedBrowsingTopicsService() = default;
~FixedBrowsingTopicsService() override = default;
bool HandleTopicsWebApi(
const url::Origin& context_origin,
content::RenderFrameHost* main_frame,
ApiCallerSource caller_source,
bool get_topics,
bool observe,
std::vector<blink::mojom::EpochTopicPtr>& topics) override {
return false;
}
int NumVersionsInEpochs(const url::Origin& main_frame_origin) const override {
return 0;
}
void GetBrowsingTopicsStateForWebUi(
bool calculate_now,
browsing_topics::mojom::PageHandler::GetBrowsingTopicsStateCallback
callback) override {
DCHECK(result_override_);
std::move(callback).Run(result_override_->Clone());
}
std::vector<privacy_sandbox::CanonicalTopic> GetTopTopicsForDisplay()
const override {
return {};
}
void ValidateCalculationSchedule() override {}
Annotator* GetAnnotator() override { return &test_annotator_; }
void ClearTopic(
const privacy_sandbox::CanonicalTopic& canonical_topic) override {}
void ClearTopicsDataForOrigin(const url::Origin& origin) override {}
void ClearAllTopicsData() override {}
void SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResultPtr result) {
result_override_ = std::move(result);
}
TestAnnotator* test_annotator() { return &test_annotator_; }
private:
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResultPtr result_override_;
TestAnnotator test_annotator_;
};
} // namespace
class BrowsingTopicsInternalsBrowserTestBase : public InProcessBrowserTest {
public:
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void FlushForTesting() {
BrowsingTopicsInternalsUI* internals_ui =
static_cast<BrowsingTopicsInternalsUI*>(
web_contents()->GetWebUI()->GetController());
BrowsingTopicsInternalsPageHandler* page_handler =
internals_ui->page_handler();
page_handler->FlushForTesting();
}
// Executing javascript in the WebUI requires using an isolated world in which
// to execute the script because WebUI has a default CSP policy denying
// "eval()", which is what EvalJs uses under the hood.
std::string EvalJsInWebUI(const std::string& script) {
return content::EvalJs(web_contents()->GetPrimaryMainFrame(), script,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
/*world_id=*/1)
.ExtractString();
}
std::string GetModelInfoContent() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
if (document.querySelector('#model-info-override-status-message-div').style.display !== 'none') {
result += document.querySelector('#model-info-override-status-message-div').textContent + '\n';
}
if (document.querySelector('#model-info-div').style.display !== 'none') {
result += document.querySelector('#model-version-div').textContent + '\n';
result += document.querySelector('#model-file-path-div').textContent + '\n';
}
result
)");
return html_content;
}
std::string GetHostsClassificationInputValidationError() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
let errorsDiv = document.querySelector('#hosts-classification-input-validation-error');
if (errorsDiv.style.display !== 'none') {
Array.from(errorsDiv.children).forEach((errorDiv) => {
result += errorDiv.textContent + '\n';
});
}
result
)");
return html_content;
}
std::string GetHostsClassificationResultTableContent() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
let tableWrapperDiv = document.querySelector('#hosts-classification-result-table-wrapper');
if (tableWrapperDiv.style.display === 'none') {
result
} else {
let rows = tableWrapperDiv.querySelectorAll('tr');
rows.forEach(row => {
let formattedRow = '';
let cells = row.querySelectorAll('td');
cells.forEach(cell => {
let maybeSpans = cell.querySelectorAll('span');
if (maybeSpans.length > 0) {
let formattedCell = '';
maybeSpans.forEach(span => {
formattedCell += span.textContent + ';';
});
formattedRow += formattedCell + '|';
return;
}
formattedRow += cell.textContent + '|';
});
if (formattedRow !== '') {
result += formattedRow + '\n';
}
});
}
result
)");
return html_content;
}
std::string GetTopicsStateTabContent() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
let overrideStatus = document.querySelector(
'#topics-state-override-status-message-div').textContent;
if (overrideStatus) {
result += 'overrideStatus: ' + overrideStatus + '\n';
}
if (document.querySelector('#topics-state-div').style.display === 'none') {
result
} else {
let nextCalculationTimeDiv = document.querySelector(
'#next-scheduled-calculation-time-div');
result += nextCalculationTimeDiv.textContent + '\n';
let epochDivs = document.querySelectorAll('.epoch-div');
epochDivs.forEach(epochDiv => {
result += '===== epoch =====\n';
let statusDivs = epochDiv.querySelectorAll('div');
statusDivs.forEach(statusDiv => {
result += statusDiv.textContent + '\n';
});
let rows = epochDiv.querySelectorAll('tr');
rows.forEach(row => {
let formattedRow = '';
let cells = row.querySelectorAll('td');
cells.forEach(cell => {
let maybeSpans = cell.querySelectorAll('span');
if (maybeSpans.length > 0) {
let formattedCell = '';
maybeSpans.forEach(span => {
formattedCell += span.textContent + ';';
});
formattedRow += formattedCell + '|';
return;
}
formattedRow += cell.textContent + '|';
});
if (formattedRow !== '') {
result += formattedRow + '\n';
}
});
});
result
}
)");
// Skip checking the time field as it depends on the locale.
RE2::GlobalReplace(&html_content, "time: .+",
"time: {{TIMESTAMP_TO_IGNORE}}");
return html_content;
}
std::string GetFeaturesAndParametersTabContent() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
let featureDivs = document.querySelector('.features-and-parameters-div')
.querySelectorAll('div');
featureDivs.forEach(featureDiv => {
result += featureDiv.textContent + '\n';
});
result
)");
return html_content;
}
std::string GetConsentInfoTabContent() {
std::string html_content = EvalJsInWebUI(R"(
let result = '';
let consentDivs = document.querySelector('.consent-info-div')
.querySelectorAll('div');
consentDivs.forEach(consentDiv => {
result += consentDiv.textContent + '\n';
});
result
)");
return html_content;
}
std::string BuildExpectedConsentInfoString(int consent_status_string_id,
int consent_source_string_id) {
auto* privacy_sandbox_service =
PrivacySandboxServiceFactory::GetForProfile(browser()->profile());
auto consent_text = privacy_sandbox_service->TopicsConsentLastUpdateText();
auto last_update_time = browser()->profile()->GetPrefs()->GetTime(
prefs::kPrivacySandboxTopicsConsentLastUpdateTime);
std::string expected_text =
"{topicsConsentStatusLabel} {topicsConsentStatus}\n"
"{topicsConsentSourceLabel} {topicsConsentSource}\n"
"{topicsConsentTimeLabel} {topicsConsentTime}\n"
"{topicsConsentTextLabel} {topicsConsentText}\n";
RE2::Replace(&expected_text, "{topicsConsentStatusLabel}",
l10n_util::GetStringUTF8(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_STATUS_LABEL));
RE2::Replace(
&expected_text, "{topicsConsentSourceLabel}",
l10n_util::GetStringUTF8(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_LAST_UPDATE_SOURCE_LABEL));
RE2::Replace(
&expected_text, "{topicsConsentTimeLabel}",
l10n_util::GetStringUTF8(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_LAST_UPDATE_TIME_LABEL));
RE2::Replace(
&expected_text, "{topicsConsentTextLabel}",
l10n_util::GetStringUTF8(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_LAST_UPDATE_TEXT_LABEL));
RE2::Replace(&expected_text, "{topicsConsentStatus}",
l10n_util::GetStringUTF8(consent_status_string_id));
RE2::Replace(&expected_text, "{topicsConsentSource}",
l10n_util::GetStringUTF8(consent_source_string_id));
RE2::Replace(&expected_text, "{topicsConsentTime}",
base::UTF16ToUTF8(
base::TimeFormatFriendlyDateAndTime(last_update_time)));
RE2::Replace(&expected_text, "{topicsConsentText}", consent_text);
return expected_text;
}
};
class BrowsingTopicsDisabledInternalsBrowserTest
: public BrowsingTopicsInternalsBrowserTestBase {
public:
BrowsingTopicsDisabledInternalsBrowserTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{},
/*disabled_features=*/{
network::features::kBrowsingTopics,
blink::features::kBrowsingTopicsParameters,
features::kPrivacySandboxAdsAPIsOverride,
});
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledInternalsBrowserTest,
FeaturesDisabled) {
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetFeaturesAndParametersTabContent(), R"(BrowsingTopics: disabled
PrivacySandboxAdsAPIsOverride: disabled
OverridePrivacySandboxSettingsLocalTesting: disabled
BrowsingTopicsBypassIPIsPubliclyRoutableCheck: disabled
BrowsingTopicsDocumentAPI: enabled
Configuration version: 2
BrowsingTopicsParameters: disabled
BrowsingTopicsParameters:number_of_epochs_to_expose: 3
BrowsingTopicsParameters:time_period_per_epoch: 7d-0h-0m-0s
BrowsingTopicsParameters:number_of_top_topics_per_epoch: 5
BrowsingTopicsParameters:use_random_topic_probability_percent: 5
BrowsingTopicsParameters:max_epoch_introduction_delay: 2d-0h-0m-0s
BrowsingTopicsParameters:number_of_epochs_of_observation_data_to_use_for_filtering: 3
BrowsingTopicsParameters:max_number_of_api_usage_context_domains_to_keep_per_topic: 1000
BrowsingTopicsParameters:max_number_of_api_usage_context_entries_to_load_per_epoch: 100000
BrowsingTopicsParameters:max_number_of_api_usage_context_domains_to_store_per_page_load: 30
BrowsingTopicsParameters:taxonomy_version: 2
BrowsingTopicsParameters:disabled_topics_list:
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledInternalsBrowserTest,
TopicsState_OverrideStatusMessage) {
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(
GetTopicsStateTabContent(),
R"(overrideStatus: No BrowsingTopicsService: the "BrowsingTopics" or other depend-on features are disabled.
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledInternalsBrowserTest,
ClassifierTab_OverrideStatusMessage) {
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(
GetModelInfoContent(),
R"(No BrowsingTopicsService: the "BrowsingTopics" or other depend-on features are disabled.
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsDisabledInternalsBrowserTest,
ConsentInfo_ConsentNotRequired) {
EXPECT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(kBrowsingTopicsInternalsConsentInfoUrl)));
auto consent_string = GetConsentInfoTabContent();
auto expected_string = BuildExpectedConsentInfoString(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_NOT_REQUIRED,
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_UPDATE_SOURCE_DEFAULT);
EXPECT_EQ(expected_string, consent_string);
}
class BrowsingTopicsInternalsBrowserTest
: public BrowsingTopicsInternalsBrowserTestBase {
public:
BrowsingTopicsInternalsBrowserTest() {
scoped_feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kBrowsingTopicsParameters,
{{"number_of_top_topics_per_epoch", "2"},
{"time_period_per_epoch", "15s"}}},
{network::features::kBrowsingTopics, {}},
{features::kPrivacySandboxAdsAPIsOverride, {}},
{privacy_sandbox::kPrivacySandboxSettings4,
{{"consent-required", "true"}}}},
/*disabled_features=*/{});
}
// BrowserTestBase::SetUpInProcessBrowserTestFixture
void SetUpBrowserContextKeyedServices(
content::BrowserContext* context) override {
browsing_topics::BrowsingTopicsServiceFactory::GetInstance()
->SetTestingFactory(
context, base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<FixedBrowsingTopicsService>();
}));
}
FixedBrowsingTopicsService* fixed_browsing_topics_service() {
return static_cast<FixedBrowsingTopicsService*>(
browsing_topics::BrowsingTopicsServiceFactory::GetForProfile(
browser()->profile()));
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest, FeaturesEnabled) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetFeaturesAndParametersTabContent(), R"(BrowsingTopics: enabled
PrivacySandboxAdsAPIsOverride: enabled
OverridePrivacySandboxSettingsLocalTesting: disabled
BrowsingTopicsBypassIPIsPubliclyRoutableCheck: disabled
BrowsingTopicsDocumentAPI: enabled
Configuration version: 2
BrowsingTopicsParameters: enabled
BrowsingTopicsParameters:number_of_epochs_to_expose: 3
BrowsingTopicsParameters:time_period_per_epoch: 0d-0h-0m-15s
BrowsingTopicsParameters:number_of_top_topics_per_epoch: 2
BrowsingTopicsParameters:use_random_topic_probability_percent: 5
BrowsingTopicsParameters:max_epoch_introduction_delay: 2d-0h-0m-0s
BrowsingTopicsParameters:number_of_epochs_of_observation_data_to_use_for_filtering: 3
BrowsingTopicsParameters:max_number_of_api_usage_context_domains_to_keep_per_topic: 1000
BrowsingTopicsParameters:max_number_of_api_usage_context_entries_to_load_per_epoch: 100000
BrowsingTopicsParameters:max_number_of_api_usage_context_domains_to_store_per_page_load: 30
BrowsingTopicsParameters:taxonomy_version: 2
BrowsingTopicsParameters:disabled_topics_list:
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
TopicsState_OverrideStatusMessage) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetTopicsStateTabContent(),
R"(overrideStatus: Failed to get the topics state.
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
TopicsState_Populated) {
auto state = browsing_topics::mojom::WebUIBrowsingTopicsState::New();
state->next_scheduled_calculation_time = base::Time::Now();
{
auto webui_epoch = mojom::WebUIEpoch::New();
webui_epoch->calculation_time = base::Time::Now() - base::Days(7);
webui_epoch->model_version = "2204011803";
webui_epoch->taxonomy_version = "123";
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 2;
webui_topic->topic_name = u"Acting & Theater";
webui_topic->is_real_topic = true;
webui_topic->observed_by_domains = {"111", "222", "333"};
webui_epoch->topics.push_back(std::move(webui_topic));
}
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 3;
webui_topic->topic_name = u"Comics";
webui_topic->is_real_topic = true;
webui_topic->observed_by_domains = {"444"};
webui_epoch->topics.push_back(std::move(webui_topic));
}
state->epochs.push_back(std::move(webui_epoch));
}
{
auto webui_epoch = mojom::WebUIEpoch::New();
webui_epoch->calculation_time = base::Time::Now() - base::Days(7);
webui_epoch->model_version = "2204011803";
webui_epoch->taxonomy_version = "123";
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 4;
webui_topic->topic_name = u"Concerts & Music Festivals";
webui_topic->is_real_topic = true;
webui_topic->observed_by_domains = {};
webui_epoch->topics.push_back(std::move(webui_topic));
}
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 5;
webui_topic->topic_name = u"Concerts & Music Festivals";
webui_topic->is_real_topic = false;
webui_topic->observed_by_domains = {"555"};
webui_epoch->topics.push_back(std::move(webui_topic));
}
state->epochs.push_back(std::move(webui_epoch));
}
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewBrowsingTopicsState(std::move(state)));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetTopicsStateTabContent(),
R"(Next scheduled calculation time: {{TIMESTAMP_TO_IGNORE}}
===== epoch =====
Calculation time: {{TIMESTAMP_TO_IGNORE}}
Model version: 2204011803
Taxonomy version: 123
2|Acting & Theater|Real|111;222;333;|
3|Comics|Real|444;|
===== epoch =====
Calculation time: {{TIMESTAMP_TO_IGNORE}}
Model version: 2204011803
Taxonomy version: 123
4|Concerts & Music Festivals|Real||
5|Concerts & Music Festivals|Random|555;|
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
TopicsState_CalculateNow) {
auto state = browsing_topics::mojom::WebUIBrowsingTopicsState::New();
state->next_scheduled_calculation_time = base::Time::Now();
{
auto webui_epoch = mojom::WebUIEpoch::New();
webui_epoch->calculation_time = base::Time::Now() - base::Days(7);
webui_epoch->model_version = "2204011803";
webui_epoch->taxonomy_version = "123";
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 2;
webui_topic->topic_name = u"Acting & Theater";
webui_topic->is_real_topic = true;
webui_topic->observed_by_domains = {"111", "222", "333"};
webui_epoch->topics.push_back(std::move(webui_topic));
}
state->epochs.push_back(std::move(webui_epoch));
}
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewBrowsingTopicsState(std::move(state)));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetTopicsStateTabContent(),
R"(Next scheduled calculation time: {{TIMESTAMP_TO_IGNORE}}
===== epoch =====
Calculation time: {{TIMESTAMP_TO_IGNORE}}
Model version: 2204011803
Taxonomy version: 123
2|Acting & Theater|Real|111;222;333;|
)");
auto new_state = browsing_topics::mojom::WebUIBrowsingTopicsState::New();
new_state->next_scheduled_calculation_time = base::Time::Now();
{
auto webui_epoch = mojom::WebUIEpoch::New();
webui_epoch->calculation_time = base::Time::Now() - base::Days(7);
webui_epoch->model_version = "2204011803";
webui_epoch->taxonomy_version = "123";
{
auto webui_topic = mojom::WebUITopic::New();
webui_topic->topic_id = 4;
webui_topic->topic_name = u"Concerts & Music Festivals";
webui_topic->is_real_topic = true;
webui_topic->observed_by_domains = {};
webui_epoch->topics.push_back(std::move(webui_topic));
}
new_state->epochs.push_back(std::move(webui_epoch));
}
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewBrowsingTopicsState(std::move(new_state)));
constexpr char calculate_now_script[] = R"(
document.querySelector('#calculate-now-button').click();
// Assert that buttons are disabled during a Calculate Now request.
if (!document.querySelector('#refresh-topics-state-button').disabled ||
!document.querySelector('#calculate-now-button').disabled) {
throw "Buttons should be disabled";
}
)";
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
calculate_now_script,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
/*world_id=*/1));
EXPECT_EQ(GetTopicsStateTabContent(),
R"(Next scheduled calculation time: {{TIMESTAMP_TO_IGNORE}}
===== epoch =====
Calculation time: {{TIMESTAMP_TO_IGNORE}}
Model version: 2204011803
Taxonomy version: 123
4|Concerts & Music Festivals|Real||
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
ClassifierTab_ModelUnavailable) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
// The |ModelInfo| is not set so the model will not be marked as available.
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetModelInfoContent(),
R"(Model unavailable.
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest, ClassifierTab) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
// Configure the (mock) model.
fixed_browsing_topics_service()->test_annotator()->UseModelInfo(
*optimization_guide::TestModelInfoBuilder()
.SetVersion(1)
.SetModelFilePath(
base::FilePath::FromASCII("/test_path/test_model.tflite"))
.Build());
fixed_browsing_topics_service()->test_annotator()->UseAnnotations({
{"foo1.com", {1, 2}},
{"foo2.com", {3, 4, 5}},
});
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
EXPECT_EQ(GetModelInfoContent(),
R"(Model version: 1
Model file path: /test_path/test_model.tflite
)");
constexpr char classify_hosts_script[] = R"(
document.querySelector('#input-hosts-textarea').value = 'foo1.com\nfoo2.com\n';
document.querySelector('#hosts-classification-button').click();
)";
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
classify_hosts_script,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
/*world_id=*/1));
FlushForTesting();
EXPECT_EQ(GetHostsClassificationResultTableContent(),
R"(foo1.com|1. Arts & Entertainment;2. Acting & Theater;|
foo2.com|3. Comics;4. Concerts & Music Festivals;5. Dance;|
)");
EXPECT_TRUE(GetHostsClassificationInputValidationError().empty());
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
ClassifierTab_InvalidInput) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
// Configure the (mock) model.
fixed_browsing_topics_service()->test_annotator()->UseModelInfo(
*optimization_guide::TestModelInfoBuilder()
.SetVersion(1)
.SetModelFilePath(
base::FilePath::FromASCII("/test_path/test_model.tflite"))
.Build());
fixed_browsing_topics_service()->test_annotator()->UseAnnotations({
{"foo1.com", {1, 2}},
{"foo2.com", {3, 4, 5}},
});
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
constexpr char classify_hosts_script[] = R"(
document.querySelector('#input-hosts-textarea').value = 'foo1.com\nhttps://foo1.com\nfoo1.com/path';
document.querySelector('#hosts-classification-button').click();
)";
EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
classify_hosts_script,
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
/*world_id=*/1));
EXPECT_TRUE(GetHostsClassificationResultTableContent().empty());
EXPECT_EQ(GetHostsClassificationInputValidationError(),
R"(Host "https://foo1.com" contains invalid character: "/"
Host "foo1.com/path" contains invalid character: "/"
)");
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
ConsentInfo_RequiresFragment) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(kBrowsingTopicsInternalsUrl)));
constexpr char consent_tab_display[] = R"(
let element = document.querySelector('#consent-info')
window.getComputedStyle(element).display
)";
EXPECT_EQ("none", EvalJsInWebUI(consent_tab_display))
<< "Consent info tab should be hidden if not fragment target";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(kBrowsingTopicsInternalsConsentInfoUrl)));
EXPECT_EQ("block", EvalJsInWebUI(consent_tab_display))
<< "Consent info tab should be visible if fragment target";
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
ConsentInfo_ActiveConsent) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
auto* privacy_sandbox_service =
PrivacySandboxServiceFactory::GetForProfile(browser()->profile());
privacy_sandbox_service->PromptActionOccurred(
PrivacySandboxService::PromptAction::kConsentAccepted,
PrivacySandboxService::SurfaceType::kDesktop);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(kBrowsingTopicsInternalsConsentInfoUrl)));
auto consent_string = GetConsentInfoTabContent();
auto expected_string = BuildExpectedConsentInfoString(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_ACTIVE,
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_UPDATE_SOURCE_CONFIRMATION);
EXPECT_EQ(expected_string, consent_string);
}
IN_PROC_BROWSER_TEST_F(BrowsingTopicsInternalsBrowserTest,
ConsentInfo_InactiveConsent) {
fixed_browsing_topics_service()->SetWebUIGetBrowsingTopicsStateResultOverride(
browsing_topics::mojom::WebUIGetBrowsingTopicsStateResult::
NewOverrideStatusMessage("Failed to get the topics state."));
auto* privacy_sandbox_service =
PrivacySandboxServiceFactory::GetForProfile(browser()->profile());
privacy_sandbox_service->TopicsToggleChanged(/*new_value=*/false);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(kBrowsingTopicsInternalsConsentInfoUrl)));
auto consent_string = GetConsentInfoTabContent();
auto expected_string = BuildExpectedConsentInfoString(
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_INACTIVE,
IDS_PRIVACY_SANDBOX_TOPICS_CONSENT_UPDATE_SOURCE_SETTINGS);
EXPECT_EQ(expected_string, consent_string);
}
} // namespace browsing_topics