// 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/renderer/extensions/api/identity_hooks_delegate.h"

#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/mojom/context_type.mojom.h"
#include "extensions/common/mojom/frame.mojom.h"
#include "extensions/renderer/bindings/api_binding_test_util.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/renderer/native_extension_bindings_system_test_base.h"
#include "extensions/renderer/script_context.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

using IdentityHooksDelegateTest = NativeExtensionBindingsSystemUnittest;

// Tests that the result modifier used in the getAuthToken handle request hook
// results in callback-based calls getting a response with multiple arguments
// and promise-based calls getting a response with a single object.
TEST_F(IdentityHooksDelegateTest, GetAuthToken) {
  // Initialize bindings system.
  bindings_system()->api_system()->RegisterHooksDelegate(
      "identity", std::make_unique<IdentityHooksDelegate>());
  // Register extension.
  scoped_refptr<const Extension> extension = ExtensionBuilder("testExtension")
                                                 .AddAPIPermission("identity")
                                                 .Build();
  RegisterExtension(extension);
  v8::HandleScope handle_scope(isolate());
  v8::Local<v8::Context> context = MainContext();
  ScriptContext* script_context = CreateScriptContext(
      context, extension.get(), mojom::ContextType::kPrivilegedExtension);
  script_context->set_url(extension->url());
  bindings_system()->UpdateBindingsForContext(script_context);

  constexpr char kFakeAPIResponse[] =
      R"([{"token": "foo", "grantedScopes": ["bar"]}])";

  // Calling getAuthToken without a callback should return a promise that gets
  // fulfilled with an object with the results as properties on it.
  {
    v8::Local<v8::Function> func = FunctionFromString(
        context, "(function() { return chrome.identity.getAuthToken(); })");
    v8::Local<v8::Value> result = RunFunction(func, context, 0, nullptr);
    v8::Local<v8::Promise> promise;
    ASSERT_TRUE(GetValueAs(result, &promise));
    EXPECT_EQ(v8::Promise::kPending, promise->State());

    bindings_system()->HandleResponse(last_params().request_id,
                                      /*success=*/true,
                                      ListValueFromString(kFakeAPIResponse),
                                      /*error=*/std::string());

    EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
    // Note that the object here differs slightly from the response, in that it
    // is not array wrapped and the keys become alphabetized.
    EXPECT_EQ(R"({"grantedScopes":["bar"],"token":"foo"})",
              V8ToString(promise->Result(), context));
  }

  // Calling getAuthToken with a callback should end up with the callback being
  // called with multiple parameters rather than a single object.
  {
    constexpr char kFunctionCall[] =
        R"((function(api) {
             chrome.identity.getAuthToken((token, grantedScopes) => {
               this.argument1 = token;
               this.argument2 = grantedScopes;
             });
           }))";
    v8::Local<v8::Function> func = FunctionFromString(context, kFunctionCall);
    RunFunctionOnGlobal(func, context, 0, nullptr);

    bindings_system()->HandleResponse(last_params().request_id,
                                      /*success=*/true,
                                      ListValueFromString(kFakeAPIResponse),
                                      /*error=*/std::string());

    EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(context->Global(),
                                                      context, "argument1"));
    EXPECT_EQ(R"(["bar"])", GetStringPropertyFromObject(context->Global(),
                                                        context, "argument2"));
  }
}

}  // namespace extensions
