[go: up one dir, main page]

[SendTabToSelf] Fix "empty device list" issue

Previously an empty device list would be shown when there is no valid
remote device but multiple devices are synced. i.e. All remote synced
devices are using Chrome with version earlier than M-75.

Now we will hide the entry points when there is no valid device.

(cherry picked from commit 431b4befa6e1cfda28f92893259cf764cc2f21db)

Bug: 970896
Change-Id: I64c1d58210c7ac30d1b455ea77d8aaccf6fecb27
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1652372
Reviewed-by: sebsg <sebsg@chromium.org>
Reviewed-by: Mikel Astiz <mastiz@chromium.org>
Reviewed-by: Eugene But <eugenebut@chromium.org>
Reviewed-by: Jeffrey Cohen <jeffreycohen@chromium.org>
Reviewed-by: Tanya Gupta <tgupta@chromium.org>
Commit-Queue: Tina Wang <tinazwang@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#669059}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1662973
Cr-Commit-Position: refs/branch-heads/3809@{#391}
Cr-Branched-From: d82dec1a818f378c464ba307ddd9c92133eac355-refs/heads/master@{#665002}
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
index 6700e6b..4424c63 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util.cc
@@ -42,12 +42,11 @@
          service->GetSendTabToSelfModel()->IsReady();
 }
 
-bool IsSyncingOnMultipleDevices(Profile* profile) {
-  syncer::DeviceInfoSyncService* device_sync_service =
-      DeviceInfoSyncServiceFactory::GetForProfile(profile);
-
-  return device_sync_service && device_sync_service->GetDeviceInfoTracker() &&
-         device_sync_service->GetDeviceInfoTracker()->CountActiveDevices() > 1;
+bool HasValidTargetDevice(Profile* profile) {
+  SendTabToSelfSyncService* service =
+      SendTabToSelfSyncServiceFactory::GetForProfile(profile);
+  return service && service->GetSendTabToSelfModel() &&
+         service->GetSendTabToSelfModel()->HasValidTargetDevice();
 }
 
 bool IsContentRequirementsMet(const GURL& url, Profile* profile) {
@@ -66,7 +65,7 @@
 
   // If sending is enabled, then so is receiving.
   return IsSendingEnabled() && IsUserSyncTypeActive(profile) &&
-         IsSyncingOnMultipleDevices(profile) &&
+         HasValidTargetDevice(profile) &&
          IsContentRequirementsMet(web_contents->GetURL(), profile);
 }
 
@@ -77,7 +76,7 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   return IsSendingEnabled() && IsUserSyncTypeActive(profile) &&
-         IsSyncingOnMultipleDevices(profile) &&
+         HasValidTargetDevice(profile) &&
          (IsContentRequirementsMet(web_contents->GetURL(), profile) ||
           IsContentRequirementsMet(link_url, profile));
 }
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util.h b/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
index 80f5c769..fcdf580 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
@@ -24,8 +24,8 @@
 // Returns true if the SendTabToSelf sync datatype is active.
 bool IsUserSyncTypeActive(Profile* profile);
 
-// Returns true if the user syncing on two or more devices.
-bool IsSyncingOnMultipleDevices(Profile* profile);
+// Returns true if the user has one or more valid device to share to.
+bool HasValidTargetDevice(Profile* profile);
 
 // Returns true if the tab and web content requirements are met:
 //  User is viewing an HTTP or HTTPS page.
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
index cf147eec..891d033 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.cc
@@ -12,18 +12,13 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/sync/device_info_sync_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "components/send_tab_to_self/features.h"
 #include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/send_tab_to_self/test_send_tab_to_self_model.h"
 #include "components/sync/driver/sync_driver_switches.h"
-#include "components/sync/driver/test_sync_service.h"
-#include "components/sync_device_info/device_info.h"
-#include "components/sync_device_info/device_info_sync_bridge.h"
-#include "components/sync_device_info/device_info_sync_service.h"
-#include "content/public/browser/navigation_entry.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -32,69 +27,31 @@
 
 namespace {
 
-// Mock DeviceInfoTracker class for setting active devices
-class TestDeviceInfoTracker : public syncer::DeviceInfoTracker {
+class SendTabToSelfModelMock : public TestSendTabToSelfModel {
  public:
-  TestDeviceInfoTracker() = default;
-  ~TestDeviceInfoTracker() override = default;
+  SendTabToSelfModelMock() = default;
+  ~SendTabToSelfModelMock() override = default;
 
-  void SetActiveDevices(int devices) { active_devices_ = devices; }
-
-  // DeviceInfoTracker implementation
-  bool IsSyncing() const override { return false; }
-  std::unique_ptr<syncer::DeviceInfo> GetDeviceInfo(
-      const std::string& client_id) const override {
-    return std::unique_ptr<syncer::DeviceInfo>();
-  }
-  std::vector<std::unique_ptr<syncer::DeviceInfo>> GetAllDeviceInfo()
-      const override {
-    return std::vector<std::unique_ptr<syncer::DeviceInfo>>();
-  }
-  void AddObserver(Observer* observer) override {}
-  void RemoveObserver(Observer* observer) override {}
-  int CountActiveDevices() const override { return active_devices_; }
-
-  void ForcePulseForTest() override {}
-
- protected:
-  int active_devices_;
+  bool IsReady() override { return true; }
+  bool HasValidTargetDevice() override { return true; }
 };
 
-// Mock DeviceInfoSyncService to host mocked DeviceInfoTracker
-class TestDeviceInfoSyncService : public syncer::DeviceInfoSyncService {
+class TestSendTabToSelfSyncService : public SendTabToSelfSyncService {
  public:
-  TestDeviceInfoSyncService() = default;
-  ~TestDeviceInfoSyncService() override = default;
+  TestSendTabToSelfSyncService() = default;
+  ~TestSendTabToSelfSyncService() override = default;
 
-  TestDeviceInfoTracker* GetMockDeviceInfoTracker() { return &tracker_; }
-  void SetTrackerActiveDevices(int devices) {
-    tracker_.SetActiveDevices(devices);
-  }
-
-  // DeviceInfoSyncService implementation
-  syncer::LocalDeviceInfoProvider* GetLocalDeviceInfoProvider() override {
-    return nullptr;
-  }
-  syncer::DeviceInfoTracker* GetDeviceInfoTracker() override {
-    return &tracker_;
-  }
-  base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate()
-      override {
-    return nullptr;
+  SendTabToSelfModel* GetSendTabToSelfModel() override {
+    return &send_tab_to_self_model_mock_;
   }
 
  protected:
-  TestDeviceInfoTracker tracker_;
+  SendTabToSelfModelMock send_tab_to_self_model_mock_;
 };
 
-std::unique_ptr<KeyedService> BuildMockDeviceInfoSyncService(
+std::unique_ptr<KeyedService> BuildTestSendTabToSelfSyncService(
     content::BrowserContext* context) {
-  return std::make_unique<TestDeviceInfoSyncService>();
-}
-
-std::unique_ptr<KeyedService> BuildTestSyncService(
-    content::BrowserContext* context) {
-  return std::make_unique<syncer::TestSyncService>();
+  return std::make_unique<TestSendTabToSelfSyncService>();
 }
 
 class SendTabToSelfUtilTest : public BrowserWithTestWindowTest {
@@ -104,49 +61,12 @@
 
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
-
-    test_sync_service_ = static_cast<syncer::TestSyncService*>(
-        ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile(), base::BindRepeating(&BuildTestSyncService)));
-
-    mock_device_sync_service_ = static_cast<TestDeviceInfoSyncService*>(
-        DeviceInfoSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
-            profile(), base::BindRepeating(&BuildMockDeviceInfoSyncService)));
-
     incognito_profile_ = profile()->GetOffTheRecordProfile();
     url_ = GURL("https://www.google.com");
     title_ = base::UTF8ToUTF16(base::StringPiece("Google"));
   }
 
-  // Set up all test conditions to let ShouldOfferFeature() return true
-  void SetUpAllTrueEnv() {
-    scoped_feature_list_.InitWithFeatures(
-        {switches::kSyncSendTabToSelf, kSendTabToSelfShowSendingUI}, {});
-    syncer::ModelTypeSet enabled_modeltype(syncer::SEND_TAB_TO_SELF);
-    test_sync_service_->SetPreferredDataTypes(enabled_modeltype);
-
-    mock_device_sync_service_->SetTrackerActiveDevices(2);
-
-    AddTab(browser(), url_);
-    NavigateAndCommitActiveTabWithTitle(browser(), url_, title_);
-  }
-
-  // Set up a environment in which the feature flag is disabled
-  void SetUpFeatureDisabledEnv() {
-    scoped_feature_list_.InitWithFeatures(
-        {}, {switches::kSyncSendTabToSelf, kSendTabToSelfShowSendingUI});
-    syncer::ModelTypeSet enabled_modeltype(syncer::SEND_TAB_TO_SELF);
-    test_sync_service_->SetPreferredDataTypes(enabled_modeltype);
-
-    mock_device_sync_service_->SetTrackerActiveDevices(2);
-
-    AddTab(browser(), url_);
-    NavigateAndCommitActiveTabWithTitle(browser(), url_, title_);
-  }
-
  protected:
-  syncer::TestSyncService* test_sync_service_;
-  TestDeviceInfoSyncService* mock_device_sync_service_;
   base::test::ScopedFeatureList scoped_feature_list_;
   Profile* incognito_profile_;
   GURL url_;
@@ -185,16 +105,13 @@
   EXPECT_FALSE(IsReceivingEnabled());
 }
 
-TEST_F(SendTabToSelfUtilTest, IsSyncingOnMultipleDevices_True) {
-  mock_device_sync_service_->SetTrackerActiveDevices(2);
+TEST_F(SendTabToSelfUtilTest, HasValidTargetDevice) {
+  EXPECT_FALSE(HasValidTargetDevice(profile()));
 
-  EXPECT_TRUE(IsSyncingOnMultipleDevices(profile()));
-}
+  SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile(), base::BindRepeating(&BuildTestSendTabToSelfSyncService));
 
-TEST_F(SendTabToSelfUtilTest, IsSyncingOnMultipleDevices_False) {
-  mock_device_sync_service_->SetTrackerActiveDevices(0);
-
-  EXPECT_FALSE(IsSyncingOnMultipleDevices(profile()));
+  EXPECT_TRUE(HasValidTargetDevice(profile()));
 }
 
 TEST_F(SendTabToSelfUtilTest, ContentRequirementsMet) {
diff --git a/chrome/browser/sync/test/integration/single_client_send_tab_to_self_sync_test.cc b/chrome/browser/sync/test/integration/single_client_send_tab_to_self_sync_test.cc
index 561928ba..919e3656 100644
--- a/chrome/browser/sync/test/integration/single_client_send_tab_to_self_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_send_tab_to_self_sync_test.cc
@@ -68,10 +68,11 @@
   EXPECT_TRUE(send_tab_to_self::IsUserSyncTypeActive(GetProfile(0)));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientSendTabToSelfSyncTest, IsOnMultipleDevices) {
+IN_PROC_BROWSER_TEST_F(SingleClientSendTabToSelfSyncTest,
+                       HasValidTargetDevice) {
   ASSERT_TRUE(SetupSync());
 
-  EXPECT_FALSE(send_tab_to_self::IsSyncingOnMultipleDevices(GetProfile(0)));
+  EXPECT_FALSE(send_tab_to_self::HasValidTargetDevice(GetProfile(0)));
 }
 
 IN_PROC_BROWSER_TEST_F(SingleClientSendTabToSelfSyncTest, IsFlagEnabled) {
diff --git a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
index 40a74778..0f93bdb 100644
--- a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
@@ -121,22 +121,19 @@
   EXPECT_TRUE(send_tab_to_self::IsUserSyncTypeActive(GetProfile(0)));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientSendTabToSelfSyncTest, IsOnMultipleDevices) {
+IN_PROC_BROWSER_TEST_F(TwoClientSendTabToSelfSyncTest, HasValidTargetDevice) {
   ASSERT_TRUE(SetupSync());
 
-  DeviceInfoSyncServiceFactory::GetForProfile(GetProfile(1))
-      ->GetDeviceInfoTracker()
-      ->ForcePulseForTest();
-  DeviceInfoSyncServiceFactory::GetForProfile(GetProfile(0))
-      ->GetDeviceInfoTracker()
-      ->ForcePulseForTest();
+  static_cast<send_tab_to_self::SendTabToSelfBridge*>(
+      SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile(0))
+          ->GetSendTabToSelfModel())
+      ->SetLocalDeviceNameForTest("device1");
+  static_cast<send_tab_to_self::SendTabToSelfBridge*>(
+      SendTabToSelfSyncServiceFactory::GetForProfile(GetProfile(1))
+          ->GetSendTabToSelfModel())
+      ->SetLocalDeviceNameForTest("device2");
 
-  ASSERT_TRUE(send_tab_to_self_helper::SendTabToSelfMultiDeviceActiveChecker(
-                  DeviceInfoSyncServiceFactory::GetForProfile(GetProfile(1))
-                      ->GetDeviceInfoTracker())
-                  .Wait());
-
-  EXPECT_TRUE(send_tab_to_self::IsSyncingOnMultipleDevices(GetProfile(0)));
+  EXPECT_TRUE(send_tab_to_self::HasValidTargetDevice(GetProfile(0)));
 }
 
 IN_PROC_BROWSER_TEST_F(TwoClientSendTabToSelfSyncTest,
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_device_button.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_device_button.cc
index 4d576db..029b90d 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_device_button.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_device_button.cc
@@ -15,6 +15,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/native_theme/native_theme.h"
 
 namespace send_tab_to_self {
 
@@ -30,7 +31,7 @@
   TOTAL_COUNT = 2  // Add new types above this line.
 };
 
-gfx::ImageSkia CreateDeviceIcon(DeviceIconType icon_type, bool enabled = true) {
+gfx::ImageSkia CreateDeviceIcon(DeviceIconType icon_type) {
   const gfx::VectorIcon* vector_icon;
   switch (icon_type) {
     case DeviceIconType::DESKTOP:
@@ -42,7 +43,7 @@
     default:
       vector_icon = &kSendTabToSelfIcon;
   }
-  SkColor icon_color = enabled ? gfx::kChromeIconGrey : gfx::kGoogleGrey500;
+  SkColor icon_color = ui::NativeTheme::kColorId_DefaultIconColor;
   return gfx::CreateVectorIcon(*vector_icon, kPrimaryIconSize, icon_color);
 }
 
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.cc b/components/send_tab_to_self/send_tab_to_self_bridge.cc
index 654bdc3..0fdbe15 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -441,6 +441,13 @@
   return change_processor()->IsTrackingMetadata();
 }
 
+bool SendTabToSelfBridge::HasValidTargetDevice() {
+  if (ShouldUpdateTargetDeviceNameToCacheInfoMap()) {
+    SetTargetDeviceNameToCacheInfoMap();
+  }
+  return target_device_name_to_cache_info_.size() > 0;
+}
+
 std::map<std::string, TargetDeviceInfo>
 SendTabToSelfBridge::GetTargetDeviceNameToCacheInfoMap() {
   if (ShouldUpdateTargetDeviceNameToCacheInfoMap()) {
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.h b/components/send_tab_to_self/send_tab_to_self_bridge.h
index b3028c10..7e817b1 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.h
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.h
@@ -82,6 +82,7 @@
   void DismissEntry(const std::string& guid) override;
   void MarkEntryOpened(const std::string& guid) override;
   bool IsReady() override;
+  bool HasValidTargetDevice() override;
   std::map<std::string, TargetDeviceInfo> GetTargetDeviceNameToCacheInfoMap()
       override;
 
diff --git a/components/send_tab_to_self/send_tab_to_self_model.h b/components/send_tab_to_self/send_tab_to_self_model.h
index 4497384a..bae3766 100644
--- a/components/send_tab_to_self/send_tab_to_self_model.h
+++ b/components/send_tab_to_self/send_tab_to_self_model.h
@@ -72,6 +72,9 @@
   void AddObserver(SendTabToSelfModelObserver* observer);
   void RemoveObserver(SendTabToSelfModelObserver* observer);
 
+  // Returns true if the user has valid target device.
+  virtual bool HasValidTargetDevice() = 0;
+
   // Returns a map of the name of possible target devices for the send tab to
   // self feature to their cache guid. This is a thin layer on top of
   // DeviceInfoTracker.
diff --git a/components/send_tab_to_self/test_send_tab_to_self_model.cc b/components/send_tab_to_self/test_send_tab_to_self_model.cc
index 294b3ce..6883553 100644
--- a/components/send_tab_to_self/test_send_tab_to_self_model.cc
+++ b/components/send_tab_to_self/test_send_tab_to_self_model.cc
@@ -35,6 +35,10 @@
   return false;
 }
 
+bool TestSendTabToSelfModel::HasValidTargetDevice() {
+  return false;
+}
+
 std::map<std::string, TargetDeviceInfo>
 TestSendTabToSelfModel::GetTargetDeviceNameToCacheInfoMap() {
   return {};
diff --git a/components/send_tab_to_self/test_send_tab_to_self_model.h b/components/send_tab_to_self/test_send_tab_to_self_model.h
index 80e8853..bdc0e4f 100644
--- a/components/send_tab_to_self/test_send_tab_to_self_model.h
+++ b/components/send_tab_to_self/test_send_tab_to_self_model.h
@@ -36,6 +36,7 @@
   void MarkEntryOpened(const std::string& guid) override;
 
   bool IsReady() override;
+  bool HasValidTargetDevice() override;
   std::map<std::string, TargetDeviceInfo> GetTargetDeviceNameToCacheInfoMap()
       override;
 };
diff --git a/ios/chrome/browser/send_tab_to_self/BUILD.gn b/ios/chrome/browser/send_tab_to_self/BUILD.gn
index 81cf622..25d9dd0 100644
--- a/ios/chrome/browser/send_tab_to_self/BUILD.gn
+++ b/ios/chrome/browser/send_tab_to_self/BUILD.gn
@@ -52,8 +52,11 @@
     "//base",
     "//base/test:test_support",
     "//components/send_tab_to_self",
+    "//components/send_tab_to_self:test_support",
     "//components/sync",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/sync:test_support",
     "//testing/gtest",
     "//third_party/ocmock",
     "//url",
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.h b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
index c029019..63c5637 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.h
@@ -25,8 +25,8 @@
 // Returns true if the SendTabToSelf sync datatype is active.
 bool IsUserSyncTypeActive(ios::ChromeBrowserState* browser_state);
 
-// Returns true if the user syncing on two or more devices.
-bool IsSyncingOnMultipleDevices(ios::ChromeBrowserState* browser_state);
+// Returns true if there is valid device.
+bool HasValidTargetDevice(ios::ChromeBrowserState* browser_state);
 
 // Returns true if the tab and web content requirements are met:
 //  User is viewing an HTTP or HTTPS page.
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.mm b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.mm
index 311788e..96f3d9f54 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.mm
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util.mm
@@ -54,11 +54,11 @@
          service->GetSendTabToSelfModel()->IsReady();
 }
 
-bool IsSyncingOnMultipleDevices(ios::ChromeBrowserState* browser_state) {
-  syncer::DeviceInfoSyncService* device_sync_service =
-      DeviceInfoSyncServiceFactory::GetForBrowserState(browser_state);
-  return device_sync_service && device_sync_service->GetDeviceInfoTracker() &&
-         device_sync_service->GetDeviceInfoTracker()->CountActiveDevices() > 1;
+bool HasValidTargetDevice(ios::ChromeBrowserState* browser_state) {
+  SendTabToSelfSyncService* service =
+      SendTabToSelfSyncServiceFactory::GetForBrowserState(browser_state);
+  return service && service->GetSendTabToSelfModel() &&
+         service->GetSendTabToSelfModel()->HasValidTargetDevice();
 }
 
 bool IsContentRequirementsMet(const GURL& url,
@@ -73,7 +73,7 @@
                         const GURL& url) {
   // If sending is enabled, then so is receiving.
   return IsSendingEnabled() && IsUserSyncTypeActive(browser_state) &&
-         IsSyncingOnMultipleDevices(browser_state) &&
+         HasValidTargetDevice(browser_state) &&
          IsContentRequirementsMet(url, browser_state);
 }
 
diff --git a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.mm b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.mm
index f1ce137..54182dc9 100644
--- a/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.mm
+++ b/ios/chrome/browser/send_tab_to_self/send_tab_to_self_util_unittest.mm
@@ -7,8 +7,11 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/send_tab_to_self/features.h"
+#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
+#include "components/send_tab_to_self/test_send_tab_to_self_model.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#include "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
@@ -18,6 +21,36 @@
 
 namespace send_tab_to_self {
 
+// TODO (crbug/974040): update TestSendTabToSelfModel and delete this class
+class SendTabToSelfModelMock : public TestSendTabToSelfModel {
+ public:
+  SendTabToSelfModelMock() = default;
+  ~SendTabToSelfModelMock() override = default;
+
+  bool IsReady() override { return true; }
+  bool HasValidTargetDevice() override { return true; }
+};
+
+// TODO (crbug/974040): Move TestSendTabToSelfSyncService to components and
+// reuse in both ios/chrome and chrome tests
+class TestSendTabToSelfSyncService : public SendTabToSelfSyncService {
+ public:
+  TestSendTabToSelfSyncService() = default;
+  ~TestSendTabToSelfSyncService() override = default;
+
+  SendTabToSelfModel* GetSendTabToSelfModel() override {
+    return &send_tab_to_self_model_mock_;
+  }
+
+ protected:
+  SendTabToSelfModelMock send_tab_to_self_model_mock_;
+};
+
+std::unique_ptr<KeyedService> BuildTestSendTabToSelfSyncService(
+    web::BrowserState* context) {
+  return std::make_unique<TestSendTabToSelfSyncService>();
+}
+
 class SendTabToSelfUtilTest : public PlatformTest {
  public:
   SendTabToSelfUtilTest() {
@@ -39,6 +72,15 @@
   std::unique_ptr<ios::ChromeBrowserState> browser_state_;
 };
 
+TEST_F(SendTabToSelfUtilTest, HasValidTargetDevice) {
+  EXPECT_FALSE(HasValidTargetDevice(browser_state()));
+
+  SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
+      browser_state(), base::BindRepeating(&BuildTestSendTabToSelfSyncService));
+
+  EXPECT_TRUE(HasValidTargetDevice(browser_state()));
+}
+
 TEST_F(SendTabToSelfUtilTest, AreFlagsEnabled) {
   scoped_task_environment_.RunUntilIdle();
   scoped_feature_list_.InitWithFeatures(