chromium/chrome/browser/extensions/desktop_android/desktop_android_extensions_browsertest.cc

// Copyright 2024 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/threading/thread_restrictions.h"
#include "chrome/browser/extensions/desktop_android/desktop_android_extension_system.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#include "chrome/test/base/android/android_browser_test.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/file_util.h"
#include "extensions/test/result_catcher.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"

namespace extensions {

namespace {

// Attempts to load (parse) an extension from the given `file_path`, returning
// it on success. On failure, adds a test failure.
scoped_refptr<const Extension> LoadExtensionFromDirectory(
    const base::FilePath& file_path) {
  base::ScopedAllowBlockingForTesting allow_blocking;

  std::string load_error;
  scoped_refptr<const Extension> extension = file_util::LoadExtension(
      file_path, mojom::ManifestLocation::kUnpacked, 0, &load_error);
  if (!extension) {
    ADD_FAILURE() << "Failed to create extension: " << load_error;
    return nullptr;
  }

  return extension;
}

}  // namespace

class DesktopAndroidExtensionsBrowserTest : public AndroidBrowserTest {
 public:
  DesktopAndroidExtensionsBrowserTest() = default;
  ~DesktopAndroidExtensionsBrowserTest() override = default;

  content::WebContents* GetActiveWebContents() {
    return chrome_test_utils::GetActiveWebContents(this);
  }

  void SetUpOnMainThread() override {
    AndroidBrowserTest::SetUpOnMainThread();

    host_resolver()->AddRule("*", "127.0.0.1");
    ASSERT_TRUE(embedded_test_server()->Start());
  }
};

// The following is a simple test exercising a basic navigation and script
// injection. This doesn't exercise any extensions logic, but ensures Chrome
// successfully starts and can navigate the web.
IN_PROC_BROWSER_TEST_F(DesktopAndroidExtensionsBrowserTest, SanityCheck) {
  ASSERT_EQ(TabModelList::models().size(), 1u);

  EXPECT_TRUE(content::NavigateToURL(
      GetActiveWebContents(),
      embedded_test_server()->GetURL("example.com", "/title1.html")));

  EXPECT_EQ("This page has no title.",
            content::EvalJs(GetActiveWebContents(), "document.body.innerText"));
}

// Tests the ability to parse and validate a simple extension.
IN_PROC_BROWSER_TEST_F(DesktopAndroidExtensionsBrowserTest,
                       ParseAndValidateASimpleExtension) {
  static constexpr char kManifest[] =
      R"({
           "name": "Test Extension",
           "version": "0.1",
           "manifest_version": 3
         })";

  TestExtensionDir test_dir;
  test_dir.WriteManifest(kManifest);

  scoped_refptr<const Extension> extension =
      LoadExtensionFromDirectory(test_dir.UnpackedPath());
  ASSERT_TRUE(extension);

  // Validate the fields in the extension.
  EXPECT_EQ("Test Extension", extension->name());
  EXPECT_EQ("0.1", extension->version().GetString());
  EXPECT_EQ(3, extension->manifest_version());
}

// Tests the adding an extension to the registry and navigating to a
// corresponding page in the extension, verifying the expected content.
IN_PROC_BROWSER_TEST_F(DesktopAndroidExtensionsBrowserTest,
                       NavigateToExtensionPage) {
  static constexpr char kManifest[] =
      R"({
           "name": "Test Extension",
           "version": "0.1",
           "manifest_version": 3
         })";
  static constexpr char kPageHtml[] =
      R"(<html>
           Hello, world
         </html>)";

  TestExtensionDir test_dir;
  test_dir.WriteManifest(kManifest);
  test_dir.WriteFile(FILE_PATH_LITERAL("page.html"), kPageHtml);

  scoped_refptr<const Extension> extension =
      LoadExtensionFromDirectory(test_dir.UnpackedPath());
  ASSERT_TRUE(extension);

  content::BrowserContext* browser_context =
      GetActiveWebContents()->GetBrowserContext();

  auto* android_system = static_cast<DesktopAndroidExtensionSystem*>(
      ExtensionSystem::Get(browser_context));
  ASSERT_TRUE(android_system);
  android_system->AddExtension(extension);

  GURL extension_page = extension->GetResourceURL("page.html");

  EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(), extension_page));
  EXPECT_EQ(extension_page, GetActiveWebContents()->GetLastCommittedURL());
  EXPECT_EQ("Hello, world",
            content::EvalJs(GetActiveWebContents(), "document.body.innerText"));
}

}  // namespace extensions