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

#include "components/autofill/core/browser/credit_card_field.h"

#include <memory>
#include <vector>

#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_scanner.h"
#include "components/autofill/core/common/form_field_data.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::ASCIIToUTF16;

namespace autofill {

class CreditCardFieldTestBase {
 public:
  CreditCardFieldTestBase() {}
  ~CreditCardFieldTestBase() {}

 protected:
  std::vector<std::unique_ptr<AutofillField>> list_;
  std::unique_ptr<const CreditCardField> field_;
  FieldCandidatesMap field_candidates_map_;

  // Parses the contents of |list_| as a form, and stores the result into
  // |field_|.
  void Parse() {
    AutofillScanner scanner(list_);
    std::unique_ptr<FormField> field = CreditCardField::Parse(&scanner);
    field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release()));
  }

  void MultipleParses() {
    std::unique_ptr<FormField> field;

    AutofillScanner scanner(list_);
    while (!scanner.IsEnd()) {
      field = CreditCardField::Parse(&scanner);
      field_ = base::WrapUnique(static_cast<CreditCardField*>(field.release()));
      if (field_ == nullptr) {
        scanner.Advance();
      } else {
        AddClassifications();
      }
    }
  }

  // Associates fields with their corresponding types, based on the previous
  // call to Parse().
  void AddClassifications() {
    return field_->AddClassifications(&field_candidates_map_);
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTestBase);
};

class CreditCardFieldTest : public CreditCardFieldTestBase,
                            public testing::Test {
 public:
  CreditCardFieldTest() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTest);
};

TEST_F(CreditCardFieldTest, Empty) {
  Parse();
  ASSERT_EQ(nullptr, field_.get());
}

TEST_F(CreditCardFieldTest, NonParse) {
  list_.push_back(std::make_unique<AutofillField>());
  Parse();
  ASSERT_EQ(nullptr, field_.get());
}

TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month1")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year2")));

  Parse();
  ASSERT_EQ(nullptr, field_.get());
}

TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number1")));

  Parse();
  ASSERT_EQ(nullptr, field_.get());
}

TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number1")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month2")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year3")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month2")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year3")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseFullCreditCard) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year")));

  field.label = ASCIIToUTF16("Verification");
  field.name = ASCIIToUTF16("verification");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc")));

  field.form_control_type = "select-one";
  field.label = ASCIIToUTF16("Card Type");
  field.name = ASCIIToUTF16("card_type");
  field.option_contents.push_back(ASCIIToUTF16("visa"));
  field.option_values.push_back(ASCIIToUTF16("visa"));
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("type")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("type")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_TYPE,
            field_candidates_map_[ASCIIToUTF16("type")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
            field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseExpMonthYear) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number2")));

  field.label = ASCIIToUTF16("ExpDate Month / Year");
  field.name = ASCIIToUTF16("ExpDate");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month3")));

  field.label = ASCIIToUTF16("ExpDate Month / Year");
  field.name = ASCIIToUTF16("ExpDate");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year4")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month3")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year4")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year4")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseExpMonthYear2) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number2")));

  field.label = ASCIIToUTF16("Expiration date Month / Year");
  field.name = ASCIIToUTF16("ExpDate");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month3")));

  field.label = ASCIIToUTF16("Expiration date Month / Year");
  field.name = ASCIIToUTF16("ExpDate");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year4")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month3")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year4")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year4")].BestHeuristicType());
}

typedef struct {
  const std::string cc_fields_form_control_type;
  const std::string label;
  const int max_length;
  const ServerFieldType expected_prediction;
} ParseExpFieldTestCase;

class ParseExpFieldTest : public CreditCardFieldTestBase,
                          public testing::TestWithParam<ParseExpFieldTestCase> {
};

TEST_P(ParseExpFieldTest, ParseExpField) {
  auto test_case = GetParam();
  // Clean up after previous test cases.
  list_.clear();
  field_.reset();
  field_candidates_map_.clear();

  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  field.form_control_type = test_case.cc_fields_form_control_type;
  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("num2")));

  field.label = ASCIIToUTF16(test_case.label);
  if (test_case.max_length != 0) {
    field.max_length = test_case.max_length;
  }
  field.name = ASCIIToUTF16("cc_exp");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("exp3")));

  Parse();

  // Assists in identifing which case has failed.
  SCOPED_TRACE(test_case.expected_prediction);
  SCOPED_TRACE(test_case.max_length);
  SCOPED_TRACE(test_case.label);

  if (test_case.expected_prediction == UNKNOWN_TYPE) {
    // Expect failure and continue to next test case.
    // The expiry date is a required field for credit card forms, and thus the
    // parse sets |field_| to nullptr.
    EXPECT_EQ(nullptr, field_.get());
    return;
  }

  // Ensure that the form was determined as valid.
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("num2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("num2")].BestHeuristicType());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("exp3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(test_case.expected_prediction,
            field_candidates_map_[ASCIIToUTF16("exp3")].BestHeuristicType());
}

INSTANTIATE_TEST_CASE_P(
    CreditCardFieldTest,
    ParseExpFieldTest,
    testing::Values(
        // CC fields input_type="text"
        // General label, no maxlength.
        ParseExpFieldTestCase{"text", "Expiration Date", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, maxlength 4.
        ParseExpFieldTestCase{"text", "Expiration Date", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // General label, maxlength 5.
        ParseExpFieldTestCase{"text", "Expiration Date", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // General label, maxlength 6.
        ParseExpFieldTestCase{"text", "Expiration Date", 6,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, maxlength 7.
        ParseExpFieldTestCase{"text", "Expiration Date", 7,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, large maxlength.
        ParseExpFieldTestCase{"text", "Expiration Date", 12,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},

        // Unsupported maxlength, general label.
        ParseExpFieldTestCase{"text", "Expiration Date", 3, UNKNOWN_TYPE},
        // Unsupported maxlength, two digit year label.
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 3,
                              UNKNOWN_TYPE},
        // Unsupported maxlength, four digit year label.
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 3,
                              UNKNOWN_TYPE},

        // Two digit year, simple label.
        ParseExpFieldTestCase{"text", "MM / YY", 0,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash (MM/YY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 0,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, no slash (MMYY).
        ParseExpFieldTestCase{"text", "Expiration Date (MMYY)", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash and maxlength (MM/YY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash and large maxlength (MM/YY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YY)", 12,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},

        // Four digit year, simple label.
        ParseExpFieldTestCase{"text", "MM / YYYY", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash (MM/YYYY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, no slash (MMYYYY).
        ParseExpFieldTestCase{"text", "Expiration Date (MMYYYY)", 6,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash and maxlength (MM/YYYY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 7,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash and large maxlength (MM/YYYY).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 12,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},

        // Four digit year label with restrictive maxlength (4).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Four digit year label with restrictive maxlength (5).
        ParseExpFieldTestCase{"text", "Expiration Date (MM/YYYY)", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},

        // CC fields input_type="number"
        // General label, no maxlength.
        ParseExpFieldTestCase{"number", "Expiration Date", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, maxlength 4.
        ParseExpFieldTestCase{"number", "Expiration Date", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // General label, maxlength 5.
        ParseExpFieldTestCase{"number", "Expiration Date", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // General label, maxlength 6.
        ParseExpFieldTestCase{"number", "Expiration Date", 6,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, maxlength 7.
        ParseExpFieldTestCase{"number", "Expiration Date", 7,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // General label, large maxlength.
        ParseExpFieldTestCase{"number", "Expiration Date", 12,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},

        // Unsupported maxlength, general label.
        ParseExpFieldTestCase{"number", "Expiration Date", 3, UNKNOWN_TYPE},
        // Unsupported maxlength, two digit year label.
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 3,
                              UNKNOWN_TYPE},
        // Unsupported maxlength, four digit year label.
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 3,
                              UNKNOWN_TYPE},

        // Two digit year, simple label.
        ParseExpFieldTestCase{"number", "MM / YY", 0,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash (MM/YY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 0,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, no slash (MMYY).
        ParseExpFieldTestCase{"number", "Expiration Date (MMYY)", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash and maxlength (MM/YY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Two digit year, with slash and large maxlength (MM/YY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YY)", 12,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},

        // Four digit year, simple label.
        ParseExpFieldTestCase{"number", "MM / YYYY", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash (MM/YYYY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 0,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, no slash (MMYYYY).
        ParseExpFieldTestCase{"number", "Expiration Date (MMYYYY)", 6,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash and maxlength (MM/YYYY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 7,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},
        // Four digit year, with slash and large maxlength (MM/YYYY).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 12,
                              CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR},

        // Four digit year label with restrictive maxlength (4).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 4,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR},
        // Four digit year label with restrictive maxlength (5).
        ParseExpFieldTestCase{"number", "Expiration Date (MM/YYYY)", 5,
                              CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}));

TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name");
  field.name = ASCIIToUTF16("ccfullname");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());
}

// Verifies that <input type="month"> controls are able to be parsed correctly.
TEST_F(CreditCardFieldTest, ParseMonthControl) {
  FormFieldData field;

  field.form_control_type = "text";
  field.label = ASCIIToUTF16("Card number:");
  field.name = ASCIIToUTF16("ccnumber");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number1")));

  field.form_control_type = "month";
  field.label = ASCIIToUTF16("Expiration date:");
  field.name = ASCIIToUTF16("ccexp");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("date2")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("date2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("date2")].BestHeuristicType());
}

// Verify that heuristics <input name="ccyear" maxlength="2"/> considers
// *maxlength* attribute while parsing 2 Digit expiration year.
TEST_F(CreditCardFieldTest, ParseCreditCardExpYear_2DigitMaxLength) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number")));

  field.label = ASCIIToUTF16("Expiration Date");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month")));

  field.name = ASCIIToUTF16("ccyear");
  field.max_length = 2;
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_2_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseCreditCardNumberWithSplit) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number_q1");
  field.max_length = 4;
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number1")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number_q2");
  field.max_length = 4;
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number2")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number_q3");
  field.max_length = 4;
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number3")));

  // For last credit card number input field it simply ignores the |max_length|
  // attribute. So even having a very big number, does not conside it an invalid
  // split for autofilling.
  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number_q4");
  field.max_length = 20;
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number4")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month5")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year6")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number1")].BestHeuristicType());
  EXPECT_EQ(0U, list_[0]->credit_card_number_offset());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType());
  EXPECT_EQ(4U, list_[1]->credit_card_number_offset());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType());
  EXPECT_EQ(8U, list_[2]->credit_card_number_offset());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number4")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number4")].BestHeuristicType());
  EXPECT_EQ(12U, list_[3]->credit_card_number_offset());

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month5")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month5")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year6")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year6")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseMultipleCreditCardNumbers) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number2")));

  field.label = ASCIIToUTF16("Confirm Card Number");
  field.name = ASCIIToUTF16("confirm_card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number3")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month4")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year5")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number2")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month4")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month4")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year5")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year5")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseFirstAndLastNames) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("First Name on Card");
  field.name = ASCIIToUTF16("cc-fname");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name1")));

  field.label = ASCIIToUTF16("Last Name");
  field.name = ASCIIToUTF16("cc-lname");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("name2")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number3")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month4")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("year5")));

  Parse();
  ASSERT_NE(nullptr, field_.get());
  AddClassifications();

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FIRST,
            field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_LAST,
            field_candidates_map_[ASCIIToUTF16("name2")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number3")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number3")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month4")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month4")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year5")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year5")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseConsecutiveCvc) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year")));

  field.label = ASCIIToUTF16("Verification");
  field.name = ASCIIToUTF16("verification");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc")));

  field.label = ASCIIToUTF16("Verification");
  field.name = ASCIIToUTF16("verification");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc2")));

  MultipleParses();

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
            field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc2")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
            field_candidates_map_[ASCIIToUTF16("cvc2")].BestHeuristicType());
}

TEST_F(CreditCardFieldTest, ParseNonConsecutiveCvc) {
  FormFieldData field;
  field.form_control_type = "text";

  field.label = ASCIIToUTF16("Name on Card");
  field.name = ASCIIToUTF16("name_on_card");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("name")));

  field.label = ASCIIToUTF16("Card Number");
  field.name = ASCIIToUTF16("card_number");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("number")));

  field.label = ASCIIToUTF16("Exp Month");
  field.name = ASCIIToUTF16("ccmonth");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("month")));

  field.label = ASCIIToUTF16("Exp Year");
  field.name = ASCIIToUTF16("ccyear");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("year")));

  field.label = ASCIIToUTF16("Verification");
  field.name = ASCIIToUTF16("verification");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc")));

  field.label = ASCIIToUTF16("Unknown");
  field.name = ASCIIToUTF16("unknown");
  list_.push_back(
      std::make_unique<AutofillField>(field, ASCIIToUTF16("unknown")));

  field.label = ASCIIToUTF16("Verification");
  field.name = ASCIIToUTF16("verification");
  list_.push_back(std::make_unique<AutofillField>(field, ASCIIToUTF16("cvc2")));

  MultipleParses();

  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NAME_FULL,
            field_candidates_map_[ASCIIToUTF16("name")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("number")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            field_candidates_map_[ASCIIToUTF16("number")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("month")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_MONTH,
            field_candidates_map_[ASCIIToUTF16("month")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("year")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
            field_candidates_map_[ASCIIToUTF16("year")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc")) !=
              field_candidates_map_.end());
  EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
            field_candidates_map_[ASCIIToUTF16("cvc")].BestHeuristicType());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("unknown")) ==
              field_candidates_map_.end());
  ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("cvc2")) ==
              field_candidates_map_.end());
}

}  // namespace autofill
