// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/android_autofill/browser/form_field_data_android.h"

#include <memory>

#include "base/test/bind.h"
#include "components/android_autofill/browser/android_autofill_bridge_factory.h"
#include "components/android_autofill/browser/form_data_android.h"
#include "components/android_autofill/browser/mock_form_field_data_android_bridge.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/autofill_test_utils.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/unique_ids.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace autofill {
namespace {

using ::autofill::test::FormFieldDataEq;

FormFieldData CreateTestField() {
  FormFieldData f;
  f.set_name(u"SomeName");
  f.set_name_attribute(f.name());
  f.set_id_attribute(u"some_id");
  f.set_form_control_type(FormControlType::kInputText);
  return f;
}

class FormFieldDataAndroidTest : public ::testing::Test {
 public:
  FormFieldDataAndroidTest() = default;
  ~FormFieldDataAndroidTest() override = default;

  void SetUp() override {
    AndroidAutofillBridgeFactory::GetInstance()
        .SetFormFieldDataAndroidTestingFactory(base::BindLambdaForTesting(
            [this]() -> std::unique_ptr<FormFieldDataAndroidBridge> {
              auto bridge = std::make_unique<MockFormFieldDataAndroidBridge>();
              last_bridge_ = bridge.get();
              return bridge;
            }));
  }

  void TearDown() override {
    last_bridge_ = nullptr;
    AndroidAutofillBridgeFactory::GetInstance()
        .SetFormFieldDataAndroidTestingFactory({});
  }

 protected:
  MockFormFieldDataAndroidBridge& bridge() { return *last_bridge_; }

 private:
  raw_ptr<MockFormFieldDataAndroidBridge> last_bridge_ = nullptr;
};

// Tests that the equality operator of FieldTypes requires that all FieldTypes
// members match the FieldType it is compared with.
TEST_F(FormFieldDataAndroidTest, FieldTypesEquality) {
  using FieldTypes = FormFieldDataAndroid::FieldTypes;

  const FieldTypes mixed_types(/*heuristic_type=*/USERNAME,
                               /*server_type=*/NAME_FIRST,
                               /*overall_type=*/"NAME_FIRST",
                               /*server_predictions=*/{NAME_FIRST});
  const FieldTypes same_types(/*heuristic_type=*/USERNAME,
                              /*server_type=*/USERNAME,
                              /*overall_type=*/"USERNAME",
                              /*server_predictions=*/{USERNAME});
  EXPECT_NE(mixed_types, USERNAME);
  EXPECT_NE(mixed_types, NAME_FIRST);
  EXPECT_NE(same_types, NAME_FIRST);
  EXPECT_EQ(same_types, USERNAME);
}

// Tests that updating the autofill types calls the Java bridge.
TEST_F(FormFieldDataAndroidTest, UpdateAutofillTypes) {
  FormFieldData field;
  FormFieldDataAndroid field_android(&field);
  EXPECT_CALL(bridge(), UpdateFieldTypes);
  field_android.UpdateFieldTypes(FormFieldDataAndroid::FieldTypes());
}

// Tests that calling `UpdateAutofillTypes` updates the AutofillTypes member.
TEST_F(FormFieldDataAndroidTest, UpdateAutofillTypesUpdatesFieldTypes) {
  FormFieldData field;
  FormFieldDataAndroid field_android(&field);
  EXPECT_NE(field_android.field_types(), NAME_FIRST);
  field_android.UpdateFieldTypes(FormFieldDataAndroid::FieldTypes(NAME_FIRST));
  EXPECT_EQ(field_android.field_types(), NAME_FIRST);
}

// Tests that updating the field value calls the Java bridge and also updates
// the underlying `FormFieldData` object.
TEST_F(FormFieldDataAndroidTest, OnFormFieldDidChange) {
  constexpr std::u16string_view kSampleValue = u"SomeValue";

  FormFieldData field;
  field.set_is_autofilled_according_to_renderer(true);
  FormFieldDataAndroid field_android(&field);
  EXPECT_CALL(bridge(), UpdateValue(kSampleValue));
  field_android.OnFormFieldDidChange(kSampleValue);
  EXPECT_FALSE(field.is_autofilled_according_to_renderer());
  EXPECT_EQ(field.value(), kSampleValue);
}

// Tests that updating the field visibility calls the Java bridge and also
// updates the underlying `FormFieldData` object.
TEST_F(FormFieldDataAndroidTest, OnFormFieldVisibilityDidChange) {
  FormFieldData field;
  field.set_is_focusable(false);
  field.set_role(FormFieldData::RoleAttribute::kOther);
  EXPECT_FALSE(field.is_focusable());

  FormFieldDataAndroid field_android(&field);
  FormFieldData field_copy = field;

  // A field with `is_focusable=true` and a non-presentation role is focusable
  // in Autofill terms and therefore visible in Android Autofill terms.
  EXPECT_CALL(bridge(), UpdateFocusable(true));
  field_copy.set_is_focusable(true);
  field_android.OnFormFieldVisibilityDidChange(field_copy);
  EXPECT_THAT(field, FormFieldDataEq(field_copy));
}

// Tests that field similarity checks include name, name_attribute, id_attribute
// and form control type.
TEST_F(FormFieldDataAndroidTest, SimilarFieldsAs) {
  FormFieldData f1 = CreateTestField();
  FormFieldData f2 = CreateTestField();
  FormFieldDataAndroid af(&f1);

  // If the fields are the same, they are also similar.
  EXPECT_TRUE(af.SimilarFieldAs(f2));

  // If names differ, they are not similar.
  f2.set_name(f1.name() + u"x");
  EXPECT_FALSE(af.SimilarFieldAs(f2));

  // If name attributes differ, they are not similar.
  f2 = f1;
  f2.set_name_attribute(f1.name_attribute() + u"x");
  EXPECT_FALSE(af.SimilarFieldAs(f2));

  // If id attributes differ, they are not similar.
  f2 = f1;
  f2.set_id_attribute(f1.id_attribute() + u"x");
  EXPECT_FALSE(af.SimilarFieldAs(f2));

  // If form control types differ, they are not similar.
  f2 = f1;
  f2.set_form_control_type(FormControlType::kInputPassword);
  EXPECT_FALSE(af.SimilarFieldAs(f2));

  // If global ids differ, they are not similar.
  f2 = f1;
  f2.set_renderer_id(FieldRendererId(f1.renderer_id().value() + 1));
  EXPECT_FALSE(af.SimilarFieldAs(f2));
}

// Tests that field labels are similar if they have the same value or were
// inferred from the same source and that source is not a label tag.
TEST_F(FormFieldDataAndroidTest, SimilarFieldsAs_Labels) {
  FormFieldData f1 = CreateTestField();
  FormFieldData f2 = CreateTestField();
  FormFieldDataAndroid af(&f1);

  f1.set_label(u"SomeLabel");
  f1.set_label_source(FormFieldData::LabelSource::kTdTag);
  f2.set_label(f1.label());
  f2.set_label_source(FormFieldData::LabelSource::kAriaLabel);

  EXPECT_TRUE(af.SimilarFieldAs(f2));

  // Not similar because both label text and label source differ.
  f2.set_label(f1.label() + u"x");
  EXPECT_FALSE(af.SimilarFieldAs(f2));

  // Similar because the label source are equal not not a label tag.
  f2.set_label_source(f1.label_source());
  EXPECT_TRUE(af.SimilarFieldAs(f2));

  // Not similar because the labels differ and the label sources are label tags.
  f1.set_label_source(FormFieldData::LabelSource::kLabelTag);
  f2.set_label_source(FormFieldData::LabelSource::kLabelTag);
  EXPECT_FALSE(af.SimilarFieldAs(f2));
}

}  // namespace
}  // namespace autofill
