blob: cd947a8eda9ac0fdfd6a36bd2d99ee4702cd9e31 [file]
// 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 "third_party/blink/renderer/core/mobile_metrics/tap_friendliness_checker.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "third_party/blink/public/common/input/web_coalesced_input_event.h"
#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/testing/task_environment.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
#include "third_party/googletest/src/googlemock/include/gmock/gmock-matchers.h"
#include "ui/gfx/geometry/point_f.h"
namespace blink {
static constexpr char kBaseUrl[] = "http://www.test.com/";
static constexpr int kDeviceWidth = 480;
static constexpr int kDeviceHeight = 800;
static constexpr float kMinimumZoom = 0.25f;
static constexpr float kMaximumZoom = 5;
class TapFriendlinessCheckerTest : public testing::Test {
protected:
void TearDown() override {
ThreadState::Current()->CollectAllGarbageForTesting();
url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
recorder_ = nullptr;
}
void TapAt(int x, int y) {
gfx::PointF pos(x, y);
WebGestureEvent tap_event(WebInputEvent::Type::kGestureTap,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests(),
WebGestureDevice::kTouchscreen);
tap_event.SetPositionInWidget(pos);
tap_event.SetPositionInScreen(pos);
helper_->LocalMainFrame()->GetFrame()->GetEventHandler().HandleGestureEvent(
tap_event);
}
static void ConfigureAndroidSettings(WebSettings* settings) {
settings->SetViewportEnabled(true);
settings->SetViewportMetaEnabled(true);
}
ukm::TestUkmRecorder* GetUkmRecorder() { return recorder_.get(); }
void LoadHTML(const std::string& html, float device_scale = 1.0) {
helper_ = std::make_unique<frame_test_helpers::WebViewHelper>();
helper_->Initialize(nullptr, nullptr, ConfigureAndroidSettings);
helper_->GetWebView()->MainFrameWidget()->SetDeviceScaleFactorForTesting(
device_scale);
helper_->Resize(gfx::Size(kDeviceWidth, kDeviceHeight));
helper_->GetWebView()->GetPage()->SetDefaultPageScaleLimits(kMinimumZoom,
kMaximumZoom);
helper_->GetWebView()
->GetPage()
->GetSettings()
.SetShrinksViewportContentToFit(true);
helper_->GetWebView()->GetPage()->GetSettings().SetViewportStyle(
mojom::blink::ViewportStyle::kMobile);
helper_->LoadAhem();
frame_test_helpers::LoadHTMLString(helper_->GetWebView()->MainFrameImpl(),
html,
url_test_helpers::ToKURL(kBaseUrl));
recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
protected:
test::TaskEnvironment task_environment_;
std::unique_ptr<frame_test_helpers::WebViewHelper> helper_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> recorder_;
};
TEST_F(TapFriendlinessCheckerTest, NoTapTarget) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(5, 5);
ASSERT_EQ(recorder->entries_count(), 0u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 0u);
}
TEST_F(TapFriendlinessCheckerTest, TapTargetExists) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
<button style="width: 400px; height: 400px">
button
</a>
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(200, 200);
ASSERT_EQ(recorder->entries_count(), 1u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 1u);
ASSERT_EQ(entries[0]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_TRUE(entries[0]->metrics.empty());
}
TEST_F(TapFriendlinessCheckerTest, ClickThreeTimes) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
<button style="width: 400px; height: 400px">
button
</a>
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(200, 200);
TapAt(100, 300);
TapAt(250, 150);
ASSERT_EQ(recorder->entries_count(), 3u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 3u);
ASSERT_TRUE(entries[0]->metrics.empty());
ASSERT_EQ(entries[0]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_TRUE(entries[1]->metrics.empty());
ASSERT_EQ(entries[1]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_TRUE(entries[2]->metrics.empty());
ASSERT_EQ(entries[2]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
}
TEST_F(TapFriendlinessCheckerTest, SmallTapTarget) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
<button style="margin: 190px; width: 20px; height: 20px">
button
</a>
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(200, 200);
ASSERT_EQ(recorder->entries_count(), 1u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 1u);
ASSERT_EQ(entries[0]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_EQ(entries[0]->metrics.size(), 1u);
auto it = entries[0]->metrics.find(
ukm::builders::MobileFriendliness_TappedBadTargets::kTooSmallNameHash);
ASSERT_EQ(it->second, 1);
}
TEST_F(TapFriendlinessCheckerTest, CloseDisplayEdgeTapTarget) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
<button style="margin-left: 190px; width: 200px; height: 20px">
button
</a>
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(200, 10);
TapAt(200, 150); // Miss tap, should be ignored.
ASSERT_EQ(recorder->entries_count(), 1u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 1u);
ASSERT_EQ(entries[0]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_EQ(entries[0]->metrics.size(), 1u);
auto it = entries[0]->metrics.find(
ukm::builders::MobileFriendliness_TappedBadTargets::
kCloseDisplayEdgeNameHash);
ASSERT_EQ(it->second, 1);
}
TEST_F(TapFriendlinessCheckerTest, SmallAndCloseDisplayEdgeTapTarget) {
LoadHTML(R"HTML(
<html>
<head>
<meta name="viewport" content="width=400, initial-scale=1">
</head>
<body style="font-size: 18px; margin: 0px">
<button style="margin-left: 190px; width: 20px; height: 20px">
button
</a>
</body>
</html>
)HTML");
ukm::TestUkmRecorder* recorder = GetUkmRecorder();
TapAt(200, 10);
TapAt(200, 150); // Miss tap, should be ignored.
ASSERT_EQ(recorder->entries_count(), 1u);
auto entries =
recorder->GetEntriesByName("MobileFriendliness.TappedBadTargets");
ASSERT_EQ(entries.size(), 1u);
ASSERT_EQ(entries[0]->event_hash,
ukm::builders::MobileFriendliness_TappedBadTargets::kEntryNameHash);
ASSERT_EQ(entries[0]->metrics.size(), 2u);
auto close_it = entries[0]->metrics.find(
ukm::builders::MobileFriendliness_TappedBadTargets::
kCloseDisplayEdgeNameHash);
ASSERT_EQ(close_it->second, 1);
auto small_it = entries[0]->metrics.find(
ukm::builders::MobileFriendliness_TappedBadTargets::kTooSmallNameHash);
ASSERT_EQ(small_it->second, 1);
}
} // namespace blink