chromium/chrome/browser/ash/file_manager/open_with_browser_browsertest.cc

// 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 "chrome/browser/ash/file_manager/open_with_browser.h"

#include "ash/constants/ash_features.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/launch_result_type.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/web_applications/test/profile_test_helper.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/filename_util.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/blink/public/common/features.h"

namespace file_manager::util {

namespace {

// Returns full test file path to the given |file_name|.
base::FilePath GetTestFilePath(const std::string& file_name) {
  // Get the path to file manager's test data directory.
  base::FilePath source_dir;
  CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_dir));
  base::FilePath test_data_dir = source_dir.AppendASCII("chrome")
                                     .AppendASCII("test")
                                     .AppendASCII("data")
                                     .AppendASCII("chromeos")
                                     .AppendASCII("file_manager");
  return test_data_dir.Append(base::FilePath::FromUTF8Unsafe(file_name));
}

}  // namespace

using base::test::RunClosure;
using testing::_;

// Profile type to test. Provided to OpenWithBrowserBrowserTest via
// profile_type().
enum class TestProfileType {
  kRegular,
  kIncognito,
  kGuest,
};

struct TestCase {
  explicit TestCase(const TestProfileType profile_type)
      : profile_type(profile_type) {}

  // Show the startup browser. Navigating to a URL should work whether a browser
  // window is already opened or not. Provided to OpenWithBrowserBrowserTest via
  // startup_browser().
  TestCase& WithStartupBrowser() {
    startup_browser = true;
    return *this;
  }

  TestProfileType profile_type;
  bool startup_browser = false;
};

std::string PostTestCaseName(const ::testing::TestParamInfo<TestCase>& test) {
  std::string result;
  switch (test.param.profile_type) {
    case TestProfileType::kRegular:
      result = "Regular";
      break;
    case TestProfileType::kIncognito:
      result = "Incognito";
      break;
    case TestProfileType::kGuest:
      result = "Guest";
      break;
  }

  if (test.param.startup_browser) {
    result += "_WithStartupBrowser";
  }

  return result;
}

class OpenWithBrowserBrowserTest
    : public InProcessBrowserTest,
      public ::testing::WithParamInterface<TestCase> {
 public:
  OpenWithBrowserBrowserTest() = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    if (profile_type() == TestProfileType::kGuest) {
      ConfigureCommandLineForGuestMode(command_line);
    } else if (profile_type() == TestProfileType::kIncognito) {
      command_line->AppendSwitch(::switches::kIncognito);
    }
    if (!startup_browser()) {
      command_line->AppendSwitch(::switches::kNoStartupWindow);
    }
    InProcessBrowserTest::SetUpCommandLine(command_line);
  }

  TestProfileType profile_type() const { return GetParam().profile_type; }

  bool startup_browser() const { return GetParam().startup_browser; }

  Profile* profile() const {
    if (browser()) {
      return browser()->profile();
    }
    return ProfileManager::GetActiveUserProfile();
  }

 protected:
  storage::FileSystemURL PathToFileSystemURL(base::FilePath path) {
    return storage::FileSystemURL::CreateForTest(
        kTestStorageKey, storage::kFileSystemTypeExternal, path);
  }

  const blink::StorageKey kTestStorageKey =
      blink::StorageKey::CreateFromStringForTesting("chrome://file-manager");

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_P(OpenWithBrowserBrowserTest, OpenTextFile) {
  // For a given txt file, generate its FileSystemURL.
  const base::FilePath test_file_path = GetTestFilePath("text.txt");
  storage::FileSystemURL test_file_url = PathToFileSystemURL(test_file_path);

  // file: URL of the test file, as opened in the browser.
  GURL page_url = net::FilePathToFileURL(test_file_url.path());
  content::TestNavigationObserver navigation_observer(page_url);
  navigation_observer.StartWatchingNewWebContents();
  OpenFileWithAppOrBrowser(profile(), test_file_url, "view-in-browser");
  navigation_observer.Wait();
  ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
}

INSTANTIATE_TEST_SUITE_P(
    All,
    OpenWithBrowserBrowserTest,
    ::testing::Values(
        TestCase(TestProfileType::kRegular),
        TestCase(TestProfileType::kRegular).WithStartupBrowser(),
        TestCase(TestProfileType::kIncognito),
        TestCase(TestProfileType::kIncognito).WithStartupBrowser(),
        TestCase(TestProfileType::kGuest),
        TestCase(TestProfileType::kGuest).WithStartupBrowser()),
    &PostTestCaseName);

struct HostedAppTestCase {
  const std::string name;
  const std::string app_id;
  const std::string file_name;
};

std::string AppendTestCaseName(
    const ::testing::TestParamInfo<HostedAppTestCase>& test) {
  return test.param.name;
}

class OpenHostedFileWithAppBrowserBaseTest : public InProcessBrowserTest {
 public:
  OpenHostedFileWithAppBrowserBaseTest() {
    scoped_feature_list_.InitWithFeatures(
        {blink::features::kDesktopPWAsTabStrip},
        {features::kDesktopPWAsTabStripSettings});
  }
  ~OpenHostedFileWithAppBrowserBaseTest() override = default;

 protected:
  storage::FileSystemURL PathToFileSystemURL(base::FilePath path) {
    return storage::FileSystemURL::CreateForTest(
        kTestStorageKey, storage::kFileSystemTypeExternal, path);
  }

  Profile* profile() const {
    if (browser()) {
      return browser()->profile();
    }
    return ProfileManager::GetActiveUserProfile();
  }

  void SetUpAppInAppService(const std::string& app_id,
                            apps::Readiness readiness) {
    std::vector<apps::AppPtr> apps;
    apps::AppPtr app = std::make_unique<apps::App>(apps::AppType::kWeb, app_id);
    app->app_id = app_id;
    app->readiness = readiness;
    apps.push_back(std::move(app));
    apps::AppServiceProxyFactory::GetForProfile(profile())->OnApps(
        std::move(apps), apps::AppType::kWeb,
        false /* should_notify_initialized */);
  }

  const storage::FileSystemURL CreateHostedFile(const std::string& file_name) {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
    const base::FilePath test_file_path = temp_dir_.GetPath().Append(file_name);
    EXPECT_TRUE(base::WriteFile(
        test_file_path,
        base::StrCat({"{\"url\":\"", kTestHostedURL.spec(), "\"}"})));
    return PathToFileSystemURL(test_file_path);
  }

  void OpenURLAndExpectAppToBeOpened(
      const storage::FileSystemURL& test_file_url) {
    base::RunLoop run_loop;
    base::MockCallback<LaunchAppCallback> mock_callback;
    EXPECT_CALL(mock_callback, Run(_))
        .WillOnce(RunClosure(run_loop.QuitClosure()));
    OpenFileWithAppOrBrowser(profile(), test_file_url, "view-in-browser",
                             mock_callback.Get());
    run_loop.Run();
  }

  void OpenURLAndExpectBrowserToBeOpened(
      const storage::FileSystemURL& test_file_url) {
    content::TestNavigationObserver navigation_observer(kTestHostedURL);
    navigation_observer.StartWatchingNewWebContents();
    OpenFileWithAppOrBrowser(profile(), test_file_url, "view-in-browser");
    navigation_observer.Wait();
    EXPECT_EQ(navigation_observer.last_navigation_url(), kTestHostedURL);
  }

  const blink::StorageKey kTestStorageKey =
      blink::StorageKey::CreateFromStringForTesting("chrome://file-manager");
  base::ScopedTempDir temp_dir_;
  const GURL kTestHostedURL = GURL("https://docs.google.com/test-id");

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

class OpenHostedFileWithAppBrowserTest
    : public OpenHostedFileWithAppBrowserBaseTest,
      public ::testing::WithParamInterface<HostedAppTestCase> {};

IN_PROC_BROWSER_TEST_P(OpenHostedFileWithAppBrowserTest,
                       AppIsAvailableAndReady) {
  const HostedAppTestCase& test_case = GetParam();
  const storage::FileSystemURL test_file_url =
      CreateHostedFile(test_case.file_name);
  SetUpAppInAppService(test_case.app_id, apps::Readiness::kReady);
  OpenURLAndExpectAppToBeOpened(test_file_url);
}

IN_PROC_BROWSER_TEST_P(OpenHostedFileWithAppBrowserTest, AppIsNotInstalled) {
  const HostedAppTestCase& test_case = GetParam();
  const storage::FileSystemURL test_file_url =
      CreateHostedFile(test_case.file_name);
  OpenURLAndExpectBrowserToBeOpened(test_file_url);
}

IN_PROC_BROWSER_TEST_P(OpenHostedFileWithAppBrowserTest,
                       AppIsUninstalledByUser) {
  const HostedAppTestCase& test_case = GetParam();
  const storage::FileSystemURL test_file_url =
      CreateHostedFile(test_case.file_name);
  SetUpAppInAppService(test_case.app_id, apps::Readiness::kUninstalledByUser);
  OpenURLAndExpectBrowserToBeOpened(test_file_url);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    OpenHostedFileWithAppBrowserTest,
    ::testing::Values(HostedAppTestCase{.name = "Docs",
                                        .app_id = web_app::kGoogleDocsAppId,
                                        .file_name = "doc.gdoc"},
                      HostedAppTestCase{.name = "Sheets",
                                        .app_id = web_app::kGoogleSheetsAppId,
                                        .file_name = "sheet.gsheet"},
                      HostedAppTestCase{.name = "Slides",
                                        .app_id = web_app::kGoogleSlidesAppId,
                                        .file_name = "slide.gslides"}),
    &AppendTestCaseName);

using OpenHostedFileWithoutAppBrowserTest =
    OpenHostedFileWithAppBrowserBaseTest;

IN_PROC_BROWSER_TEST_F(OpenHostedFileWithoutAppBrowserTest,
                       HostedDocWithoutApp) {
  const storage::FileSystemURL test_file_url = CreateHostedFile("form.gform");
  OpenURLAndExpectBrowserToBeOpened(test_file_url);
}

}  // namespace file_manager::util