// Copyright (c) 2012 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 <map>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/guid.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/tuple.h"
#include "chrome/browser/ui/autofill/autofill_dialog_controller_impl.h"
#include "chrome/browser/ui/autofill/autofill_dialog_view.h"
#include "chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h"
#include "chrome/browser/ui/autofill/mock_new_credit_card_bubble_controller.h"
#include "chrome/browser/ui/autofill/test_generated_credit_card_bubble_controller.h"
#include "chrome/browser/webdata/web_data_service_factory.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
#include "components/autofill/content/browser/wallet/full_wallet.h"
#include "components/autofill/content/browser/wallet/gaia_account.h"
#include "components/autofill/content/browser/wallet/instrument.h"
#include "components/autofill/content/browser/wallet/mock_wallet_client.h"
#include "components/autofill/content/browser/wallet/wallet_address.h"
#include "components/autofill/content/browser/wallet/wallet_service_url.h"
#include "components/autofill/content/browser/wallet/wallet_test_util.h"
#include "components/autofill/core/browser/autofill_metrics.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_data.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "grit/webkit_resources.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"

#if defined(OS_WIN)
#include "ui/base/win/scoped_ole_initializer.h"
#endif

namespace autofill {

namespace {

using testing::_;

const char kFakeEmail[] = "user@chromium.org";
const char kFakeFingerprintEncoded[] = "CgVaAwiACA==";
const char kEditedBillingAddress[] = "123 edited billing address";
const char* kFieldsFromPage[] =
    { "email",
      "cc-name",
      "cc-number",
      "cc-exp-month",
      "cc-exp-year",
      "cc-csc",
      "billing name",
      "billing address-line1",
      "billing locality",
      "billing region",
      "billing postal-code",
      "billing country",
      "billing tel",
      "shipping name",
      "shipping address-line1",
      "shipping locality",
      "shipping region",
      "shipping postal-code",
      "shipping country",
      "shipping tel",
    };
const char kSettingsOrigin[] = "Chrome settings";
const char kTestCCNumberAmex[] = "376200000000002";
const char kTestCCNumberVisa[] = "4111111111111111";
const char kTestCCNumberMaster[] = "5555555555554444";
const char kTestCCNumberDiscover[] = "6011111111111117";
const char kTestCCNumberIncomplete[] = "4111111111";
// Credit card number fails Luhn check.
const char kTestCCNumberInvalid[] = "4111111111111112";

// Copies the initial values from |inputs| into |outputs|.
void CopyInitialValues(const DetailInputs& inputs, FieldValueMap* outputs) {
  for (size_t i = 0; i < inputs.size(); ++i) {
    const DetailInput& input = inputs[i];
    (*outputs)[input.type] = input.initial_value;
  }
}

scoped_ptr<wallet::WalletItems> CompleteAndValidWalletItems() {
  scoped_ptr<wallet::WalletItems> items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  items->AddAccount(wallet::GetTestGaiaAccount());
  items->AddInstrument(wallet::GetTestMaskedInstrument());
  items->AddAddress(wallet::GetTestShippingAddress());
  return items.Pass();
}

scoped_ptr<risk::Fingerprint> GetFakeFingerprint() {
  scoped_ptr<risk::Fingerprint> fingerprint(new risk::Fingerprint());
  // Add some data to the proto, else the encoded content is empty.
  fingerprint->mutable_machine_characteristics()->mutable_screen_size()->
      set_width(1024);
  return fingerprint.Pass();
}

bool HasAnyError(const ValidityMessages& messages, ServerFieldType field) {
  return !messages.GetMessageOrDefault(field).text.empty();
}

bool HasUnsureError(const ValidityMessages& messages, ServerFieldType field) {
  const ValidityMessage& message = messages.GetMessageOrDefault(field);
  return !message.text.empty() && !message.sure;
}

class TestAutofillDialogView : public AutofillDialogView {
 public:
  TestAutofillDialogView()
      : updates_started_(0), save_details_locally_checked_(true) {}
  virtual ~TestAutofillDialogView() {}

  virtual void Show() OVERRIDE {}
  virtual void Hide() OVERRIDE {}

  virtual void UpdatesStarted() OVERRIDE {
    updates_started_++;
  }

  virtual void UpdatesFinished() OVERRIDE {
    updates_started_--;
    EXPECT_GE(updates_started_, 0);
  }

  virtual void UpdateNotificationArea() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateAccountChooser() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateButtonStrip() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateOverlay() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateDetailArea() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateSection(DialogSection section) OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void UpdateErrorBubble() OVERRIDE {
    EXPECT_GE(updates_started_, 1);
  }

  virtual void FillSection(DialogSection section,
                           const DetailInput& originating_input) OVERRIDE {};
  virtual void GetUserInput(DialogSection section, FieldValueMap* output)
      OVERRIDE {
    *output = outputs_[section];
  }
  virtual TestableAutofillDialogView* GetTestableView() OVERRIDE {
    return NULL;
  }

  virtual base::string16 GetCvc() OVERRIDE { return base::string16(); }
  virtual bool HitTestInput(const DetailInput& input,
                            const gfx::Point& screen_point) OVERRIDE {
    return false;
  }

  virtual bool SaveDetailsLocally() OVERRIDE {
    return save_details_locally_checked_;
  }

  virtual const content::NavigationController* ShowSignIn() OVERRIDE {
    return NULL;
  }
  virtual void HideSignIn() OVERRIDE {}

  MOCK_METHOD0(ModelChanged, void());
  MOCK_METHOD0(UpdateForErrors, void());

  virtual void OnSignInResize(const gfx::Size& pref_size) OVERRIDE {}

  void SetUserInput(DialogSection section, const FieldValueMap& map) {
    outputs_[section] = map;
  }

  void CheckSaveDetailsLocallyCheckbox(bool checked) {
    save_details_locally_checked_ = checked;
  }

 private:
  std::map<DialogSection, FieldValueMap> outputs_;

  int updates_started_;
  bool save_details_locally_checked_;

  DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogView);
};

class TestAutofillDialogController
    : public AutofillDialogControllerImpl,
      public base::SupportsWeakPtr<TestAutofillDialogController> {
 public:
  TestAutofillDialogController(
      content::WebContents* contents,
      const FormData& form_structure,
      const GURL& source_url,
      const AutofillMetrics& metric_logger,
      const base::Callback<void(const FormStructure*)>& callback,
      MockNewCreditCardBubbleController* mock_new_card_bubble_controller)
      : AutofillDialogControllerImpl(contents,
                                     form_structure,
                                     source_url,
                                     callback),
        metric_logger_(metric_logger),
        mock_wallet_client_(
            Profile::FromBrowserContext(contents->GetBrowserContext())->
                GetRequestContext(), this, source_url),
        mock_new_card_bubble_controller_(mock_new_card_bubble_controller),
        submit_button_delay_count_(0) {}

  virtual ~TestAutofillDialogController() {}

  virtual AutofillDialogView* CreateView() OVERRIDE {
    return new testing::NiceMock<TestAutofillDialogView>();
  }

  void Init(content::BrowserContext* browser_context) {
    test_manager_.Init(
        WebDataServiceFactory::GetAutofillWebDataForProfile(
            Profile::FromBrowserContext(browser_context),
            Profile::EXPLICIT_ACCESS),
        user_prefs::UserPrefs::Get(browser_context),
        browser_context->IsOffTheRecord());
  }

  TestAutofillDialogView* GetView() {
    return static_cast<TestAutofillDialogView*>(view());
  }

  TestPersonalDataManager* GetTestingManager() {
    return &test_manager_;
  }

  wallet::MockWalletClient* GetTestingWalletClient() {
    return &mock_wallet_client_;
  }

  const GURL& open_tab_url() { return open_tab_url_; }

  void SimulateSigninError() {
    OnWalletSigninError();
  }

  // Skips past the 2 second wait between FinishSubmit and DoFinishSubmit.
  void ForceFinishSubmit() {
    DoFinishSubmit();
  }

  void SimulateSubmitButtonDelayBegin() {
    AutofillDialogControllerImpl::SubmitButtonDelayBegin();
  }

  void SimulateSubmitButtonDelayEnd() {
    AutofillDialogControllerImpl::SubmitButtonDelayEndForTesting();
  }

  using AutofillDialogControllerImpl::
      ClearLastWalletItemsFetchTimestampForTesting;

  // Returns the number of times that the submit button was delayed.
  int get_submit_button_delay_count() const {
    return submit_button_delay_count_;
  }

  MOCK_METHOD0(LoadRiskFingerprintData, void());
  using AutofillDialogControllerImpl::AccountChooserModelForTesting;
  using AutofillDialogControllerImpl::OnDidLoadRiskFingerprintData;
  using AutofillDialogControllerImpl::IsEditingExistingData;
  using AutofillDialogControllerImpl::IsSubmitPausedOn;
  using AutofillDialogControllerImpl::NOT_CHECKED;
  using AutofillDialogControllerImpl::SignedInState;

 protected:
  virtual PersonalDataManager* GetManager() const OVERRIDE {
    return const_cast<TestAutofillDialogController*>(this)->
        GetTestingManager();
  }

  virtual wallet::WalletClient* GetWalletClient() OVERRIDE {
    return &mock_wallet_client_;
  }

  virtual void OpenTabWithUrl(const GURL& url) OVERRIDE {
    open_tab_url_ = url;
  }

  virtual void ShowNewCreditCardBubble(
      scoped_ptr<CreditCard> new_card,
      scoped_ptr<AutofillProfile> billing_profile) OVERRIDE {
    mock_new_card_bubble_controller_->Show(new_card.Pass(),
                                           billing_profile.Pass());
  }

  // AutofillDialogControllerImpl calls this method before showing the dialog
  // window.
  virtual void SubmitButtonDelayBegin() OVERRIDE {
    // Do not delay enabling the submit button in testing.
    submit_button_delay_count_++;
  }

 private:
  // To specify our own metric logger.
  virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE {
    return metric_logger_;
  }

  const AutofillMetrics& metric_logger_;
  TestPersonalDataManager test_manager_;
  testing::NiceMock<wallet::MockWalletClient> mock_wallet_client_;
  GURL open_tab_url_;
  MockNewCreditCardBubbleController* mock_new_card_bubble_controller_;

  // The number of times that the submit button was delayed.
  int submit_button_delay_count_;

  DISALLOW_COPY_AND_ASSIGN(TestAutofillDialogController);
};

class AutofillDialogControllerTest : public ChromeRenderViewHostTestHarness {
 protected:
  AutofillDialogControllerTest(): form_structure_(NULL) {}

  // testing::Test implementation:
  virtual void SetUp() OVERRIDE {
    ChromeRenderViewHostTestHarness::SetUp();
    Reset();
  }

  virtual void TearDown() OVERRIDE {
    if (controller_)
      controller_->ViewClosed();
    ChromeRenderViewHostTestHarness::TearDown();
  }

  void Reset() {
    if (controller_)
      controller_->ViewClosed();

    test_generated_bubble_controller_ =
        new testing::NiceMock<TestGeneratedCreditCardBubbleController>(
            web_contents());
    ASSERT_TRUE(test_generated_bubble_controller_->IsInstalled());

    mock_new_card_bubble_controller_.reset(
        new MockNewCreditCardBubbleController);

    profile()->GetPrefs()->ClearPref(::prefs::kAutofillDialogSaveData);

    // We have to clear the old local state before creating a new one.
    scoped_local_state_.reset();
    scoped_local_state_.reset(new ScopedTestingLocalState(
        TestingBrowserProcess::GetGlobal()));

    SetUpControllerWithFormData(DefaultFormData());
  }

  FormData DefaultFormData() {
    FormData form_data;
    for (size_t i = 0; i < arraysize(kFieldsFromPage); ++i) {
      FormFieldData field;
      field.autocomplete_attribute = kFieldsFromPage[i];
      form_data.fields.push_back(field);
    }
    return form_data;
  }

  // Creates a new controller for |form_data|.
  void ResetControllerWithFormData(const FormData& form_data) {
    if (controller_)
      controller_->ViewClosed();

    base::Callback<void(const FormStructure*)> callback =
        base::Bind(&AutofillDialogControllerTest::FinishedCallback,
                   base::Unretained(this));
    controller_ = (new testing::NiceMock<TestAutofillDialogController>(
        web_contents(),
        form_data,
        GURL(),
        metric_logger_,
        callback,
        mock_new_card_bubble_controller_.get()))->AsWeakPtr();
    controller_->Init(profile());
  }

  // Creates a new controller for |form_data| and sets up some initial wallet
  // data for it.
  void SetUpControllerWithFormData(const FormData& form_data) {
    ResetControllerWithFormData(form_data);
    controller()->Show();
    if (!profile()->GetPrefs()->GetBoolean(
            ::prefs::kAutofillDialogPayWithoutWallet)) {
      EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
      controller()->OnDidFetchWalletCookieValue(std::string());
      controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
    }
  }

  // Fills the inputs in SECTION_CC with data.
  void FillCreditCardInputs() {
    FieldValueMap cc_outputs;
    const DetailInputs& cc_inputs =
        controller()->RequestedFieldsForSection(SECTION_CC);
    for (size_t i = 0; i < cc_inputs.size(); ++i) {
      cc_outputs[cc_inputs[i].type] = cc_inputs[i].type == CREDIT_CARD_NUMBER ?
          ASCIIToUTF16(kTestCCNumberVisa) : ASCIIToUTF16("11");
    }
    controller()->GetView()->SetUserInput(SECTION_CC, cc_outputs);
  }

  // Fills the inputs in SECTION_CC_BILLING with valid data.
  void FillCCBillingInputs() {
    FieldValueMap outputs;
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
    AutofillProfile full_profile(test::GetVerifiedProfile());
    CreditCard full_card(test::GetCreditCard());
    for (size_t i = 0; i < inputs.size(); ++i) {
      const ServerFieldType type = inputs[i].type;
      outputs[type] = full_profile.GetInfo(AutofillType(type), "en-US");

      if (outputs[type].empty())
        outputs[type] = full_card.GetInfo(AutofillType(type), "en-US");
    }
    controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs);
  }

  // Activates the 'Add new foo' option from the |section|'s suggestions
  // dropdown and fills the |section|'s inputs with the data from the
  // |data_model|.  If |section| is SECTION_CC, also fills in '123' for the CVC.
  void FillInputs(DialogSection section, const AutofillDataModel& data_model) {
    // Select the 'Add new foo' option.
    ui::MenuModel* model = GetMenuModelForSection(section);
    if (model)
      model->ActivatedAt(model->GetItemCount() - 2);

    // Fill the inputs.
    FieldValueMap outputs;
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(section);
    for (size_t i = 0; i < inputs.size(); ++i) {
      ServerFieldType type = inputs[i].type;
      base::string16 output;
      if (type == CREDIT_CARD_VERIFICATION_CODE)
        output = ASCIIToUTF16("123");
      else
        output = data_model.GetInfo(AutofillType(type), "en-US");
      outputs[inputs[i].type] = output;
    }
    controller()->GetView()->SetUserInput(section, outputs);
  }

  std::vector<DialogNotification> NotificationsOfType(
      DialogNotification::Type type) {
    std::vector<DialogNotification> right_type;
    const std::vector<DialogNotification>& notifications =
        controller()->CurrentNotifications();
    for (size_t i = 0; i < notifications.size(); ++i) {
      if (notifications[i].type() == type)
        right_type.push_back(notifications[i]);
    }
    return right_type;
  }

  void SwitchToAutofill() {
    ui::MenuModel* model = controller_->MenuModelForAccountChooser();
    model->ActivatedAt(model->GetItemCount() - 1);
  }

  void SwitchToWallet() {
    controller_->MenuModelForAccountChooser()->ActivatedAt(0);
  }

  void SimulateSigninError() {
    controller_->SimulateSigninError();
  }

  void UseBillingForShipping() {
    controller()->MenuModelForSection(SECTION_SHIPPING)->ActivatedAt(0);
  }

  void ValidateCCNumber(DialogSection section,
                        const std::string& cc_number,
                        bool should_pass) {
    FieldValueMap outputs;
    outputs[CREDIT_CARD_NUMBER] = UTF8ToUTF16(cc_number);
    ValidityMessages messages =
        controller()->InputsAreValid(section, outputs);
    EXPECT_EQ(should_pass, !messages.HasSureError(CREDIT_CARD_NUMBER));
  }

  void SubmitWithWalletItems(scoped_ptr<wallet::WalletItems> wallet_items) {
    controller()->OnDidGetWalletItems(wallet_items.Pass());
    AcceptAndLoadFakeFingerprint();
  }

  void AcceptAndLoadFakeFingerprint() {
    controller()->OnAccept();
    controller()->OnDidLoadRiskFingerprintData(GetFakeFingerprint().Pass());
  }

  // Returns true if the given |section| contains a field of the given |type|.
  bool SectionContainsField(DialogSection section, ServerFieldType type) {
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(section);
    for (DetailInputs::const_iterator it = inputs.begin(); it != inputs.end();
         ++it) {
      if (it->type == type)
        return true;
    }
    return false;
  }

  SuggestionsMenuModel* GetMenuModelForSection(DialogSection section) {
    ui::MenuModel* model = controller()->MenuModelForSection(section);
    return static_cast<SuggestionsMenuModel*>(model);
  }

  TestAutofillDialogController* controller() { return controller_.get(); }

  const FormStructure* form_structure() { return form_structure_; }

  TestGeneratedCreditCardBubbleController* test_generated_bubble_controller() {
    return test_generated_bubble_controller_;
  }

  const MockNewCreditCardBubbleController* mock_new_card_bubble_controller() {
    return mock_new_card_bubble_controller_.get();
  }

 private:
  void FinishedCallback(const FormStructure* form_structure) {
    form_structure_ = form_structure;
  }

#if defined(OS_WIN)
   // http://crbug.com/227221
   ui::ScopedOleInitializer ole_initializer_;
#endif

  // The controller owns itself.
  base::WeakPtr<TestAutofillDialogController> controller_;

  // Must outlive the controller.
  AutofillMetrics metric_logger_;

  // Returned when the dialog closes successfully.
  const FormStructure* form_structure_;

  // Used to monitor if the Autofill credit card bubble is shown. Owned by
  // |web_contents()|.
  TestGeneratedCreditCardBubbleController* test_generated_bubble_controller_;

  // Used to record when new card bubbles would show. Created in |Reset()|.
  scoped_ptr<MockNewCreditCardBubbleController>
      mock_new_card_bubble_controller_;

  scoped_ptr<ScopedTestingLocalState> scoped_local_state_;
};

}  // namespace

// Ensure the default ValidityMessage has the expected values.
TEST_F(AutofillDialogControllerTest, DefaultValidityMessage) {
  ValidityMessages messages;
  ValidityMessage message = messages.GetMessageOrDefault(UNKNOWN_TYPE);
  EXPECT_FALSE(message.sure);
  EXPECT_TRUE(message.text.empty());
}

// This test makes sure nothing falls over when fields are being validity-
// checked.
TEST_F(AutofillDialogControllerTest, ValidityCheck) {
  for (size_t i = SECTION_MIN; i <= SECTION_MAX; ++i) {
    DialogSection section = static_cast<DialogSection>(i);
    const DetailInputs& shipping_inputs =
        controller()->RequestedFieldsForSection(section);
    for (DetailInputs::const_iterator iter = shipping_inputs.begin();
         iter != shipping_inputs.end(); ++iter) {
      controller()->InputValidityMessage(section, iter->type, base::string16());
    }
  }
}

// Test for phone number validation.
TEST_F(AutofillDialogControllerTest, PhoneNumberValidation) {
  // Construct FieldValueMap from existing data.
  SwitchToAutofill();

  for (size_t i = 0; i < 2; ++i) {
    ServerFieldType phone = i == 0 ? PHONE_HOME_WHOLE_NUMBER :
                                     PHONE_BILLING_WHOLE_NUMBER;
    ServerFieldType address = i == 0 ? ADDRESS_HOME_COUNTRY :
                                       ADDRESS_BILLING_COUNTRY;
    DialogSection section = i == 0 ? SECTION_SHIPPING : SECTION_BILLING;

    FieldValueMap outputs;
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(section);
    AutofillProfile full_profile(test::GetVerifiedProfile());
    for (size_t i = 0; i < inputs.size(); ++i) {
      const ServerFieldType type = inputs[i].type;
      outputs[type] = full_profile.GetInfo(AutofillType(type), "en-US");
    }

    // Make sure country is United States.
    outputs[address] = ASCIIToUTF16("United States");

    // Existing data should have no errors.
    ValidityMessages messages = controller()->InputsAreValid(section, outputs);
    EXPECT_FALSE(HasAnyError(messages, phone));

    // Input an empty phone number.
    outputs[phone] = base::string16();
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_TRUE(HasUnsureError(messages, phone));

    // Input an invalid phone number.
    outputs[phone] = ASCIIToUTF16("ABC");
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_TRUE(messages.HasSureError(phone));

    // Input a local phone number.
    outputs[phone] = ASCIIToUTF16("2155546699");
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_FALSE(HasAnyError(messages, phone));

    // Input an invalid local phone number.
    outputs[phone] = ASCIIToUTF16("215554669");
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_TRUE(messages.HasSureError(phone));

    // Input an international phone number.
    outputs[phone] = ASCIIToUTF16("+33 892 70 12 39");
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_FALSE(HasAnyError(messages, phone));

    // Input an invalid international phone number.
    outputs[phone] = ASCIIToUTF16("+112333 892 70 12 39");
    messages = controller()->InputsAreValid(section, outputs);
    EXPECT_TRUE(messages.HasSureError(phone));
  }
}

TEST_F(AutofillDialogControllerTest, ExpirationDateValidity) {
  ui::ComboboxModel* exp_year_model =
      controller()->ComboboxModelForAutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR);
  ui::ComboboxModel* exp_month_model =
      controller()->ComboboxModelForAutofillType(CREDIT_CARD_EXP_MONTH);

  base::string16 default_year_value =
      exp_year_model->GetItemAt(exp_year_model->GetDefaultIndex());
  base::string16 default_month_value =
      exp_month_model->GetItemAt(exp_month_model->GetDefaultIndex());

  base::string16 other_year_value =
      exp_year_model->GetItemAt(exp_year_model->GetItemCount() - 1);
  base::string16 other_month_value =
      exp_month_model->GetItemAt(exp_month_model->GetItemCount() - 1);

  FieldValueMap outputs;
  outputs[CREDIT_CARD_EXP_MONTH] = default_month_value;
  outputs[CREDIT_CARD_EXP_4_DIGIT_YEAR] = default_year_value;

  // Expiration default values generate unsure validation errors (but not sure).
  ValidityMessages messages = controller()->InputsAreValid(SECTION_CC_BILLING,
                                                           outputs);
  EXPECT_TRUE(HasUnsureError(messages, CREDIT_CARD_EXP_4_DIGIT_YEAR));
  EXPECT_TRUE(HasUnsureError(messages, CREDIT_CARD_EXP_MONTH));

  // Expiration date with default month fails.
  outputs[CREDIT_CARD_EXP_4_DIGIT_YEAR] = other_year_value;
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, outputs);
  EXPECT_FALSE(HasUnsureError(messages, CREDIT_CARD_EXP_4_DIGIT_YEAR));
  EXPECT_TRUE(HasUnsureError(messages, CREDIT_CARD_EXP_MONTH));

  // Expiration date with default year fails.
  outputs[CREDIT_CARD_EXP_MONTH] = other_month_value;
  outputs[CREDIT_CARD_EXP_4_DIGIT_YEAR] = default_year_value;
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, outputs);
  EXPECT_TRUE(HasUnsureError(messages, CREDIT_CARD_EXP_4_DIGIT_YEAR));
  EXPECT_FALSE(HasUnsureError(messages, CREDIT_CARD_EXP_MONTH));
}

TEST_F(AutofillDialogControllerTest, BillingNameValidation) {
  // Construct FieldValueMap from AutofillProfile data.
  SwitchToAutofill();

  // Input an empty billing name.
  FieldValueMap outputs;
  outputs[NAME_BILLING_FULL] = base::string16();
  ValidityMessages messages = controller()->InputsAreValid(SECTION_BILLING,
                                                           outputs);
  EXPECT_TRUE(HasUnsureError(messages, NAME_BILLING_FULL));

  // Input a non-empty billing name.
  outputs[NAME_BILLING_FULL] = ASCIIToUTF16("Bob");
  messages = controller()->InputsAreValid(SECTION_BILLING, outputs);
  EXPECT_FALSE(HasAnyError(messages, NAME_BILLING_FULL));

  // Switch to Wallet which only considers names with with at least two names to
  // be valid.
  SwitchToWallet();

  // Setup some wallet state.
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // Input an empty billing name. Data source should not change this behavior.
  FieldValueMap wallet_outputs;
  wallet_outputs[NAME_BILLING_FULL] = base::string16();
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, wallet_outputs);
  EXPECT_TRUE(HasUnsureError(messages, NAME_BILLING_FULL));

  // Input a one name billing name. Wallet does not currently support this.
  wallet_outputs[NAME_BILLING_FULL] = ASCIIToUTF16("Bob");
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, wallet_outputs);
  EXPECT_TRUE(messages.HasSureError(NAME_BILLING_FULL));

  // Input a two name billing name.
  wallet_outputs[NAME_BILLING_FULL] = ASCIIToUTF16("Bob Barker");
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, wallet_outputs);
  EXPECT_FALSE(HasAnyError(messages, NAME_BILLING_FULL));

  // Input a more than two name billing name.
  wallet_outputs[NAME_BILLING_FULL] =
      ASCIIToUTF16("John Jacob Jingleheimer Schmidt"),
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, wallet_outputs);
  EXPECT_FALSE(HasAnyError(messages, NAME_BILLING_FULL));

  // Input a billing name with lots of crazy whitespace.
  wallet_outputs[NAME_BILLING_FULL] =
      ASCIIToUTF16("     \\n\\r John \\n  Jacob Jingleheimer \\t Schmidt  "),
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, wallet_outputs);
  EXPECT_FALSE(HasAnyError(messages, NAME_BILLING_FULL));
}

TEST_F(AutofillDialogControllerTest, CreditCardNumberValidation) {
  // Construct FieldValueMap from AutofillProfile data.
  SwitchToAutofill();

  // Should accept AMEX, Visa, Master and Discover.
  ValidateCCNumber(SECTION_CC, kTestCCNumberVisa, true);
  ValidateCCNumber(SECTION_CC, kTestCCNumberMaster, true);
  ValidateCCNumber(SECTION_CC, kTestCCNumberDiscover, true);
  ValidateCCNumber(SECTION_CC, kTestCCNumberAmex, true);
  ValidateCCNumber(SECTION_CC, kTestCCNumberIncomplete, false);
  ValidateCCNumber(SECTION_CC, kTestCCNumberInvalid, false);

  // Switch to Wallet which will not accept AMEX.
  SwitchToWallet();

  // Setup some wallet state on a merchant for which Wallet doesn't
  // support AMEX.
  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED));

  // Should accept Visa, Master and Discover, but not AMEX.
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberVisa, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberMaster, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberDiscover, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberAmex, false);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberIncomplete, false);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberInvalid, false);

  // Setup some wallet state on a merchant for which Wallet supports AMEX.
  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItems(wallet::AMEX_ALLOWED));

  // Should accept Visa, Master, Discover, and AMEX.
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberVisa, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberMaster, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberDiscover, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberAmex, true);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberIncomplete, false);
  ValidateCCNumber(SECTION_CC_BILLING, kTestCCNumberInvalid, false);
}

TEST_F(AutofillDialogControllerTest, AutofillProfiles) {
  SwitchToAutofill();
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  // Since the PersonalDataManager is empty, this should only have the
  // "use billing", "add new" and "manage" menu items.
  ASSERT_TRUE(shipping_model);
  EXPECT_EQ(3, shipping_model->GetItemCount());
  // On the other hand, the other models should be NULL when there's no
  // suggestion.
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC));
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_BILLING));

  EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(3);

  // Empty profiles are ignored.
  AutofillProfile empty_profile(base::GenerateGUID(), kSettingsOrigin);
  empty_profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John Doe"));
  controller()->GetTestingManager()->AddTestingProfile(&empty_profile);
  shipping_model = controller()->MenuModelForSection(SECTION_SHIPPING);
  ASSERT_TRUE(shipping_model);
  EXPECT_EQ(3, shipping_model->GetItemCount());

  // An otherwise full but unverified profile should be ignored.
  AutofillProfile full_profile(test::GetFullProfile());
  full_profile.set_origin("https://www.example.com");
  full_profile.SetRawInfo(ADDRESS_HOME_LINE2, base::string16());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  shipping_model = controller()->MenuModelForSection(SECTION_SHIPPING);
  ASSERT_TRUE(shipping_model);
  EXPECT_EQ(3, shipping_model->GetItemCount());

  // A full, verified profile should be picked up.
  AutofillProfile verified_profile(test::GetVerifiedProfile());
  verified_profile.SetRawInfo(ADDRESS_HOME_LINE2, base::string16());
  controller()->GetTestingManager()->AddTestingProfile(&verified_profile);
  shipping_model = controller()->MenuModelForSection(SECTION_SHIPPING);
  ASSERT_TRUE(shipping_model);
  EXPECT_EQ(4, shipping_model->GetItemCount());
}

// Makes sure that the choice of which Autofill profile to use for each section
// is sticky.
TEST_F(AutofillDialogControllerTest, AutofillProfileDefaults) {
  SwitchToAutofill();
  AutofillProfile profile(test::GetVerifiedProfile());
  AutofillProfile profile2(test::GetVerifiedProfile2());
  controller()->GetTestingManager()->AddTestingProfile(&profile);
  controller()->GetTestingManager()->AddTestingProfile(&profile2);

  // Until a selection has been made, the default shipping suggestion is the
  // first one (after "use billing").
  SuggestionsMenuModel* shipping_model =
      GetMenuModelForSection(SECTION_SHIPPING);
  EXPECT_EQ(1, shipping_model->checked_item());

  for (int i = 2; i >= 0; --i) {
    shipping_model = GetMenuModelForSection(SECTION_SHIPPING);
    shipping_model->ExecuteCommand(i, 0);
    FillCreditCardInputs();
    controller()->OnAccept();

    Reset();
    controller()->GetTestingManager()->AddTestingProfile(&profile);
    controller()->GetTestingManager()->AddTestingProfile(&profile2);
    shipping_model = GetMenuModelForSection(SECTION_SHIPPING);
    EXPECT_EQ(i, shipping_model->checked_item());
  }

  // Try again, but don't add the default profile to the PDM. The dialog
  // should fall back to the first profile.
  shipping_model->ExecuteCommand(2, 0);
  FillCreditCardInputs();
  controller()->OnAccept();
  Reset();
  controller()->GetTestingManager()->AddTestingProfile(&profile);
  shipping_model = GetMenuModelForSection(SECTION_SHIPPING);
  EXPECT_EQ(1, shipping_model->checked_item());
}

// Makes sure that a newly added Autofill profile becomes set as the default
// choice for the next run.
TEST_F(AutofillDialogControllerTest, NewAutofillProfileIsDefault) {
  SwitchToAutofill();

  AutofillProfile profile(test::GetVerifiedProfile());
  CreditCard credit_card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingProfile(&profile);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);

  // Until a selection has been made, the default suggestion is the first one.
  // For the shipping section, this follows the "use billing" suggestion.
  EXPECT_EQ(0, GetMenuModelForSection(SECTION_CC)->checked_item());
  EXPECT_EQ(1, GetMenuModelForSection(SECTION_SHIPPING)->checked_item());

  // Fill in the shipping and credit card sections with new data.
  AutofillProfile new_profile(test::GetVerifiedProfile2());
  CreditCard new_credit_card(test::GetVerifiedCreditCard2());
  FillInputs(SECTION_SHIPPING, new_profile);
  FillInputs(SECTION_CC, new_credit_card);
  controller()->GetView()->CheckSaveDetailsLocallyCheckbox(true);
  controller()->OnAccept();

  // Update the |new_profile| and |new_credit_card|'s guids to the saved ones.
  new_profile.set_guid(
      controller()->GetTestingManager()->imported_profile().guid());
  new_credit_card.set_guid(
      controller()->GetTestingManager()->imported_credit_card().guid());

  // Reload the dialog. The newly added address and credit card should now be
  // set as the defaults.
  Reset();
  controller()->GetTestingManager()->AddTestingProfile(&profile);
  controller()->GetTestingManager()->AddTestingProfile(&new_profile);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);
  controller()->GetTestingManager()->AddTestingCreditCard(&new_credit_card);

  // Until a selection has been made, the default suggestion is the first one.
  // For the shipping section, this follows the "use billing" suggestion.
  EXPECT_EQ(1, GetMenuModelForSection(SECTION_CC)->checked_item());
  EXPECT_EQ(2, GetMenuModelForSection(SECTION_SHIPPING)->checked_item());
}

TEST_F(AutofillDialogControllerTest, AutofillProfileVariants) {
  SwitchToAutofill();
  EXPECT_CALL(*controller()->GetView(), ModelChanged());
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  ASSERT_TRUE(!!shipping_model);
  EXPECT_EQ(3, shipping_model->GetItemCount());

  // Set up some variant data.
  AutofillProfile full_profile(test::GetVerifiedProfile());
  std::vector<base::string16> names;
  names.push_back(ASCIIToUTF16("John Doe"));
  names.push_back(ASCIIToUTF16("Jane Doe"));
  full_profile.SetRawMultiInfo(NAME_FULL, names);
  std::vector<base::string16> emails;
  emails.push_back(ASCIIToUTF16(kFakeEmail));
  emails.push_back(ASCIIToUTF16("admin@example.com"));
  full_profile.SetRawMultiInfo(EMAIL_ADDRESS, emails);

  // Non-default variants are ignored by the dialog.
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  EXPECT_EQ(4, shipping_model->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, SuggestValidEmail) {
  SwitchToAutofill();
  AutofillProfile profile(test::GetVerifiedProfile());
  const base::string16 kValidEmail = ASCIIToUTF16(kFakeEmail);
  profile.SetRawInfo(EMAIL_ADDRESS, kValidEmail);
  controller()->GetTestingManager()->AddTestingProfile(&profile);

  // "add", "manage", and 1 suggestion.
  EXPECT_EQ(
      3, controller()->MenuModelForSection(SECTION_BILLING)->GetItemCount());
  // "add", "manage", 1 suggestion, and "same as billing".
  EXPECT_EQ(
      4, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, DoNotSuggestInvalidEmail) {
  SwitchToAutofill();
  AutofillProfile profile(test::GetVerifiedProfile());
  profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16(".!#$%&'*+/=?^_`-@-.."));
  controller()->GetTestingManager()->AddTestingProfile(&profile);

  EXPECT_FALSE(!!controller()->MenuModelForSection(SECTION_BILLING));
  // "add", "manage", 1 suggestion, and "same as billing".
  EXPECT_EQ(
      4, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, SuggestValidAddress) {
  SwitchToAutofill();
  AutofillProfile full_profile(test::GetVerifiedProfile());
  full_profile.set_origin(kSettingsOrigin);
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  // "add", "manage", and 1 suggestion.
  EXPECT_EQ(
      3, controller()->MenuModelForSection(SECTION_BILLING)->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, DoNotSuggestInvalidAddress) {
  SwitchToAutofill();
  AutofillProfile full_profile(test::GetVerifiedProfile());
  full_profile.set_origin(kSettingsOrigin);
  full_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("C"));
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);

  EXPECT_FALSE(!!controller()->MenuModelForSection(SECTION_BILLING));
}

TEST_F(AutofillDialogControllerTest, DoNotSuggestIncompleteAddress) {
  SwitchToAutofill();
  AutofillProfile profile(test::GetVerifiedProfile());
  profile.SetRawInfo(ADDRESS_HOME_STATE, base::string16());
  controller()->GetTestingManager()->AddTestingProfile(&profile);

  EXPECT_FALSE(!!controller()->MenuModelForSection(SECTION_BILLING));
}

TEST_F(AutofillDialogControllerTest, AutofillCreditCards) {
  SwitchToAutofill();
  // Since the PersonalDataManager is empty, this should only have the
  // default menu items.
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC));

  EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(3);

  // Empty cards are ignored.
  CreditCard empty_card(base::GenerateGUID(), kSettingsOrigin);
  empty_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("John Doe"));
  controller()->GetTestingManager()->AddTestingCreditCard(&empty_card);
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC));

  // An otherwise full but unverified card should be ignored.
  CreditCard full_card(test::GetCreditCard());
  full_card.set_origin("https://www.example.com");
  controller()->GetTestingManager()->AddTestingCreditCard(&full_card);
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC));

  // A full, verified card should be picked up.
  CreditCard verified_card(test::GetCreditCard());
  verified_card.set_origin(kSettingsOrigin);
  controller()->GetTestingManager()->AddTestingCreditCard(&verified_card);
  ui::MenuModel* credit_card_model =
      controller()->MenuModelForSection(SECTION_CC);
  ASSERT_TRUE(credit_card_model);
  EXPECT_EQ(3, credit_card_model->GetItemCount());
}

// Test selecting a shipping address different from billing as address.
TEST_F(AutofillDialogControllerTest, DontUseBillingAsShipping) {
  SwitchToAutofill();
  AutofillProfile full_profile(test::GetVerifiedProfile());
  AutofillProfile full_profile2(test::GetVerifiedProfile2());
  CreditCard credit_card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  controller()->GetTestingManager()->AddTestingProfile(&full_profile2);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  shipping_model->ActivatedAt(2);

  controller()->OnAccept();
  ASSERT_EQ(20U, form_structure()->field_count());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(9)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_BILLING, form_structure()->field(9)->Type().group());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(16)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_HOME, form_structure()->field(16)->Type().group());
  base::string16 billing_state = form_structure()->field(9)->value;
  base::string16 shipping_state = form_structure()->field(16)->value;
  EXPECT_FALSE(billing_state.empty());
  EXPECT_FALSE(shipping_state.empty());
  EXPECT_NE(billing_state, shipping_state);

  EXPECT_EQ(CREDIT_CARD_NAME,
            form_structure()->field(1)->Type().GetStorableType());
  base::string16 cc_name = form_structure()->field(1)->value;
  EXPECT_EQ(NAME_FULL, form_structure()->field(6)->Type().GetStorableType());
  EXPECT_EQ(NAME_BILLING, form_structure()->field(6)->Type().group());
  base::string16 billing_name = form_structure()->field(6)->value;
  EXPECT_EQ(NAME_FULL, form_structure()->field(13)->Type().GetStorableType());
  EXPECT_EQ(NAME, form_structure()->field(13)->Type().group());
  base::string16 shipping_name = form_structure()->field(13)->value;

  EXPECT_FALSE(cc_name.empty());
  EXPECT_FALSE(billing_name.empty());
  EXPECT_FALSE(shipping_name.empty());
  // Billing name should always be the same as cardholder name.
  EXPECT_EQ(cc_name, billing_name);
  EXPECT_NE(cc_name, shipping_name);
}

// Test selecting UseBillingForShipping.
TEST_F(AutofillDialogControllerTest, UseBillingAsShipping) {
  SwitchToAutofill();
  AutofillProfile full_profile(test::GetVerifiedProfile());
  AutofillProfile full_profile2(test::GetVerifiedProfile2());
  CreditCard credit_card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  controller()->GetTestingManager()->AddTestingProfile(&full_profile2);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);

  // Test after setting use billing for shipping.
  UseBillingForShipping();

  controller()->OnAccept();
  ASSERT_EQ(20U, form_structure()->field_count());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(9)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_BILLING, form_structure()->field(9)->Type().group());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(16)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_HOME, form_structure()->field(16)->Type().group());
  base::string16 billing_state = form_structure()->field(9)->value;
  base::string16 shipping_state = form_structure()->field(16)->value;
  EXPECT_FALSE(billing_state.empty());
  EXPECT_FALSE(shipping_state.empty());
  EXPECT_EQ(billing_state, shipping_state);

  EXPECT_EQ(CREDIT_CARD_NAME,
            form_structure()->field(1)->Type().GetStorableType());
  base::string16 cc_name = form_structure()->field(1)->value;
  EXPECT_EQ(NAME_FULL, form_structure()->field(6)->Type().GetStorableType());
  EXPECT_EQ(NAME_BILLING, form_structure()->field(6)->Type().group());
  base::string16 billing_name = form_structure()->field(6)->value;
  EXPECT_EQ(NAME_FULL, form_structure()->field(13)->Type().GetStorableType());
  EXPECT_EQ(NAME, form_structure()->field(13)->Type().group());
  base::string16 shipping_name = form_structure()->field(13)->value;

  EXPECT_FALSE(cc_name.empty());
  EXPECT_FALSE(billing_name.empty());
  EXPECT_FALSE(shipping_name.empty());
  EXPECT_EQ(cc_name, billing_name);
  EXPECT_EQ(cc_name, shipping_name);
}

// Tests that shipping and billing telephone fields are supported, and filled
// in by their respective profiles. http://crbug.com/244515
TEST_F(AutofillDialogControllerTest, BillingVsShippingPhoneNumber) {
  FormFieldData shipping_tel;
  shipping_tel.autocomplete_attribute = "shipping tel";
  FormFieldData billing_tel;
  billing_tel.autocomplete_attribute = "billing tel";

  FormData form_data;
  form_data.fields.push_back(shipping_tel);
  form_data.fields.push_back(billing_tel);
  SetUpControllerWithFormData(form_data);

  SwitchToAutofill();

  // The profile that will be chosen for the shipping section.
  AutofillProfile shipping_profile(test::GetVerifiedProfile());
  // The profile that will be chosen for the billing section.
  AutofillProfile billing_profile(test::GetVerifiedProfile2());
  CreditCard credit_card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingProfile(&shipping_profile);
  controller()->GetTestingManager()->AddTestingProfile(&billing_profile);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);
  ui::MenuModel* billing_model =
      controller()->MenuModelForSection(SECTION_BILLING);
  billing_model->ActivatedAt(1);

  controller()->OnAccept();
  ASSERT_EQ(2U, form_structure()->field_count());
  EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
            form_structure()->field(0)->Type().GetStorableType());
  EXPECT_EQ(PHONE_HOME, form_structure()->field(0)->Type().group());
  EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
            form_structure()->field(1)->Type().GetStorableType());
  EXPECT_EQ(PHONE_BILLING, form_structure()->field(1)->Type().group());
  EXPECT_EQ(shipping_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
            form_structure()->field(0)->value);
  EXPECT_EQ(billing_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
            form_structure()->field(1)->value);
  EXPECT_NE(form_structure()->field(1)->value,
            form_structure()->field(0)->value);
}

TEST_F(AutofillDialogControllerTest, AcceptLegalDocuments) {
  for (size_t i = 0; i < 2; ++i) {
    SCOPED_TRACE(testing::Message() << "Case " << i);

    EXPECT_CALL(*controller()->GetTestingWalletClient(),
                AcceptLegalDocuments(_, _));
    EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_));
    EXPECT_CALL(*controller(), LoadRiskFingerprintData());

    EXPECT_TRUE(controller()->LegalDocumentLinks().empty());
    controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
    EXPECT_TRUE(controller()->LegalDocumentLinks().empty());

    scoped_ptr<wallet::WalletItems> wallet_items =
        CompleteAndValidWalletItems();
    wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
    wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
    controller()->OnDidGetWalletItems(wallet_items.Pass());
    EXPECT_FALSE(controller()->LegalDocumentLinks().empty());

    controller()->OnAccept();
    controller()->OnDidAcceptLegalDocuments();
    controller()->OnDidLoadRiskFingerprintData(GetFakeFingerprint().Pass());

    // Now try it all over again with the location disclosure already accepted.
    // Nothing should change.
    Reset();
    ListValue preexisting_list;
    preexisting_list.AppendString(kFakeEmail);
    g_browser_process->local_state()->Set(
        ::prefs::kAutofillDialogWalletLocationAcceptance,
        preexisting_list);
  }
}

TEST_F(AutofillDialogControllerTest, RejectLegalDocuments) {
  for (size_t i = 0; i < 2; ++i) {
    SCOPED_TRACE(testing::Message() << "Case " << i);

    EXPECT_CALL(*controller()->GetTestingWalletClient(),
                AcceptLegalDocuments(_, _)).Times(0);

    scoped_ptr<wallet::WalletItems> wallet_items =
        CompleteAndValidWalletItems();
    wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
    wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
    controller()->OnDidGetWalletItems(wallet_items.Pass());
    EXPECT_FALSE(controller()->LegalDocumentLinks().empty());

    controller()->OnCancel();

    // Now try it all over again with the location disclosure already accepted.
    // Nothing should change.
    Reset();
    ListValue preexisting_list;
    preexisting_list.AppendString(kFakeEmail);
    g_browser_process->local_state()->Set(
        ::prefs::kAutofillDialogWalletLocationAcceptance,
        preexisting_list);
  }
}

TEST_F(AutofillDialogControllerTest, AcceptLocationDisclosure) {
  // Check that accepting the dialog registers the user's name in the list
  // of users who have accepted the geolocation terms.
  EXPECT_TRUE(g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance)->empty());

  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  EXPECT_FALSE(controller()->LegalDocumentsText().empty());
  EXPECT_TRUE(controller()->LegalDocumentLinks().empty());
  controller()->OnAccept();

  const ListValue* list = g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance);
  ASSERT_EQ(1U, list->GetSize());
  std::string accepted_username;
  EXPECT_TRUE(list->GetString(0, &accepted_username));
  EXPECT_EQ(kFakeEmail, accepted_username);

  // Now check it still works if that list starts off with some other username
  // in it.
  Reset();
  list = g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance);
  ASSERT_TRUE(list->empty());

  std::string kOtherUsername("spouse@example.com");
  ListValue preexisting_list;
  preexisting_list.AppendString(kOtherUsername);
  g_browser_process->local_state()->Set(
      ::prefs::kAutofillDialogWalletLocationAcceptance,
      preexisting_list);

  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  EXPECT_FALSE(controller()->LegalDocumentsText().empty());
  EXPECT_TRUE(controller()->LegalDocumentLinks().empty());
  controller()->OnAccept();

  list = g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance);
  ASSERT_EQ(2U, list->GetSize());
  EXPECT_NE(list->end(), list->Find(base::StringValue(kFakeEmail)));
  EXPECT_NE(list->end(), list->Find(base::StringValue(kOtherUsername)));

  // Now check the list doesn't change if the user cancels out of the dialog.
  Reset();
  list = g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance);
  ASSERT_TRUE(list->empty());

  g_browser_process->local_state()->Set(
      ::prefs::kAutofillDialogWalletLocationAcceptance,
      preexisting_list);

  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  EXPECT_FALSE(controller()->LegalDocumentsText().empty());
  EXPECT_TRUE(controller()->LegalDocumentLinks().empty());
  controller()->OnCancel();

  list = g_browser_process->local_state()->GetList(
      ::prefs::kAutofillDialogWalletLocationAcceptance);
  ASSERT_EQ(1U, list->GetSize());
  EXPECT_NE(list->end(), list->Find(base::StringValue(kOtherUsername)));
  EXPECT_EQ(list->end(), list->Find(base::StringValue(kFakeEmail)));
}

TEST_F(AutofillDialogControllerTest, LegalDocumentOverflow) {
  for (size_t number_of_docs = 2; number_of_docs < 11; ++number_of_docs) {
    scoped_ptr<wallet::WalletItems> wallet_items =
        CompleteAndValidWalletItems();
    for (size_t i = 0; i < number_of_docs; ++i)
      wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());

    Reset();
    controller()->OnDidGetWalletItems(wallet_items.Pass());

    // The dialog is only equipped to handle 2-6 legal documents. More than
    // 6 errors out.
    if (number_of_docs <= 6U) {
      EXPECT_FALSE(controller()->LegalDocumentsText().empty());
    } else {
      EXPECT_TRUE(controller()->LegalDocumentsText().empty());
      EXPECT_EQ(1U, NotificationsOfType(
          DialogNotification::WALLET_ERROR).size());
    }
  }

  controller()->OnCancel();
}

// Makes sure the default object IDs are respected.
TEST_F(AutofillDialogControllerTest, WalletDefaultItems) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());

  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // "add", "manage", and 4 suggestions.
  EXPECT_EQ(6,
      controller()->MenuModelForSection(SECTION_CC_BILLING)->GetItemCount());
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING)->
      IsItemCheckedAt(2));
  ASSERT_FALSE(controller()->IsEditingExistingData(SECTION_CC_BILLING));
  // "use billing", "add", "manage", and 5 suggestions.
  EXPECT_EQ(8,
      controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_SHIPPING)->
      IsItemCheckedAt(4));
  ASSERT_FALSE(controller()->IsEditingExistingData(SECTION_SHIPPING));
}

// Tests that invalid and AMEX default instruments are ignored.
TEST_F(AutofillDialogControllerTest, SelectInstrument) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  // Tests if default instrument is invalid, then, the first valid instrument is
  // selected instead of the default instrument.
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentInvalid());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // 4 suggestions and "add", "manage".
  EXPECT_EQ(6,
      controller()->MenuModelForSection(SECTION_CC_BILLING)->GetItemCount());
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING)->
      IsItemCheckedAt(0));

  // Tests if default instrument is AMEX but Wallet doesn't support
  // AMEX on this merchant, then the first valid instrument is
  // selected instead of the default instrument.
  wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(
      wallet::GetTestMaskedInstrumentAmex(wallet::AMEX_DISALLOWED));
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // 4 suggestions and "add", "manage".
  EXPECT_EQ(6,
      controller()->MenuModelForSection(SECTION_CC_BILLING)->GetItemCount());
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING)->
      IsItemCheckedAt(0));

  // Tests if default instrument is AMEX and it is allowed on this merchant,
  // then it is selected.
  wallet_items = wallet::GetTestWalletItems(wallet::AMEX_ALLOWED);
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddInstrument(
      wallet::GetTestMaskedInstrumentAmex(wallet::AMEX_ALLOWED));
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // 4 suggestions and "add", "manage".
  EXPECT_EQ(6,
      controller()->MenuModelForSection(SECTION_CC_BILLING)->GetItemCount());
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING)->
      IsItemCheckedAt(2));

  // Tests if only have AMEX and invalid instrument, then "add" is selected.
  wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentInvalid());
  wallet_items->AddInstrument(
      wallet::GetTestMaskedInstrumentAmex(wallet::AMEX_DISALLOWED));

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // 2 suggestions and "add", "manage".
  EXPECT_EQ(4,
      controller()->MenuModelForSection(SECTION_CC_BILLING)->GetItemCount());
  // "add"
  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING)->
      IsItemCheckedAt(2));
}

TEST_F(AutofillDialogControllerTest, SaveAddress) {
  EXPECT_CALL(*controller()->GetView(), ModelChanged());
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::IsNull(),
                               testing::NotNull(),
                               testing::IsNull(),
                               testing::IsNull()));

  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // If there is no shipping address in wallet, it will default to
  // "same-as-billing" instead of "add-new-item". "same-as-billing" is covered
  // by the following tests. The penultimate item in the menu is "add-new-item".
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  shipping_model->ActivatedAt(shipping_model->GetItemCount() - 2);

  AutofillProfile test_profile(test::GetVerifiedProfile());
  FillInputs(SECTION_SHIPPING, test_profile);

  AcceptAndLoadFakeFingerprint();
}

TEST_F(AutofillDialogControllerTest, SaveInstrument) {
  EXPECT_CALL(*controller()->GetView(), ModelChanged());
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::NotNull(),
                               testing::IsNull(),
                               testing::IsNull(),
                               testing::IsNull()));

  FillCCBillingInputs();
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  SubmitWithWalletItems(wallet_items.Pass());
}

TEST_F(AutofillDialogControllerTest, SaveInstrumentWithInvalidInstruments) {
  EXPECT_CALL(*controller()->GetView(), ModelChanged());
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::NotNull(),
                               testing::IsNull(),
                               testing::IsNull(),
                               testing::IsNull()));

  FillCCBillingInputs();
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentInvalid());
  SubmitWithWalletItems(wallet_items.Pass());
}

TEST_F(AutofillDialogControllerTest, SaveInstrumentAndAddress) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::NotNull(),
                               testing::NotNull(),
                               testing::IsNull(),
                               testing::IsNull()));

  FillCCBillingInputs();
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  SubmitWithWalletItems(wallet_items.Pass());
}

MATCHER(IsUpdatingExistingData, "updating existing Wallet data") {
  return !arg->object_id().empty();
}

MATCHER(UsesLocalBillingAddress, "uses the local billing address") {
  return arg->address_line_1() == ASCIIToUTF16(kEditedBillingAddress);
}

// Tests that when using billing address for shipping, and there is no exact
// matched shipping address, then a shipping address should be added.
TEST_F(AutofillDialogControllerTest, BillingForShipping) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::IsNull(),
                               testing::NotNull(),
                               testing::IsNull(),
                               testing::IsNull()));

  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  // Select "Same as billing" in the address menu.
  UseBillingForShipping();

  AcceptAndLoadFakeFingerprint();
}

// Tests that when using billing address for shipping, and there is an exact
// matched shipping address, then a shipping address should not be added.
TEST_F(AutofillDialogControllerTest, BillingForShippingHasMatch) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(_, _, _, _)).Times(0);

  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  scoped_ptr<wallet::WalletItems::MaskedInstrument> instrument =
      wallet::GetTestMaskedInstrument();
  // Copy billing address as shipping address, and assign an id to it.
  scoped_ptr<wallet::Address> shipping_address(
      new wallet::Address(instrument->address()));
  shipping_address->set_object_id("shipping_address_id");
  wallet_items->AddAddress(shipping_address.Pass());
  wallet_items->AddInstrument(instrument.Pass());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());

  controller()->OnDidGetWalletItems(wallet_items.Pass());
  // Select "Same as billing" in the address menu.
  UseBillingForShipping();

  AcceptAndLoadFakeFingerprint();
}

// Test that the local view contents is used when saving a new instrument and
// the user has selected "Same as billing".
TEST_F(AutofillDialogControllerTest, SaveInstrumentSameAsBilling) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  ui::MenuModel* model = controller()->MenuModelForSection(SECTION_CC_BILLING);
  model->ActivatedAt(model->GetItemCount() - 2);

  FieldValueMap outputs;
  const DetailInputs& inputs =
      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
  AutofillProfile full_profile(test::GetVerifiedProfile());
  CreditCard full_card(test::GetCreditCard());
  for (size_t i = 0; i < inputs.size(); ++i) {
    const ServerFieldType type = inputs[i].type;
    if (type == ADDRESS_BILLING_LINE1)
      outputs[type] = ASCIIToUTF16(kEditedBillingAddress);
    else
      outputs[type] = full_profile.GetInfo(AutofillType(type), "en-US");

    if (outputs[type].empty())
      outputs[type] = full_card.GetInfo(AutofillType(type), "en-US");
  }
  controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs);

  controller()->OnAccept();

  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(testing::NotNull(),
                               UsesLocalBillingAddress(),
                               testing::IsNull(),
                               testing::IsNull()));
  AcceptAndLoadFakeFingerprint();
}

TEST_F(AutofillDialogControllerTest, CancelNoSave) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              SaveToWalletMock(_, _, _, _)).Times(0);

  EXPECT_CALL(*controller()->GetView(), ModelChanged());

  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED));
  controller()->OnCancel();
}

// Checks that clicking the Manage menu item opens a new tab with a different
// URL for Wallet and Autofill.
TEST_F(AutofillDialogControllerTest, ManageItem) {
  AutofillProfile full_profile(test::GetVerifiedProfile());
  full_profile.set_origin(kSettingsOrigin);
  full_profile.SetRawInfo(ADDRESS_HOME_LINE2, base::string16());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  SwitchToAutofill();

  SuggestionsMenuModel* shipping = GetMenuModelForSection(SECTION_SHIPPING);
  shipping->ExecuteCommand(shipping->GetItemCount() - 1, 0);
  GURL autofill_manage_url = controller()->open_tab_url();
  EXPECT_EQ("chrome", autofill_manage_url.scheme());

  SwitchToWallet();
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  controller()->SuggestionItemSelected(shipping, shipping->GetItemCount() - 1);
  GURL wallet_manage_addresses_url = controller()->open_tab_url();
  EXPECT_EQ("https", wallet_manage_addresses_url.scheme());

  SuggestionsMenuModel* billing = GetMenuModelForSection(SECTION_CC_BILLING);
  controller()->SuggestionItemSelected(billing, billing->GetItemCount() - 1);
  GURL wallet_manage_instruments_url = controller()->open_tab_url();
  EXPECT_EQ("https", wallet_manage_instruments_url.scheme());

  EXPECT_NE(autofill_manage_url, wallet_manage_instruments_url);
  EXPECT_NE(wallet_manage_instruments_url, wallet_manage_addresses_url);
}

// Tests that adding an autofill profile and then submitting works.
TEST_F(AutofillDialogControllerTest, AddAutofillProfile) {
  SwitchToAutofill();
  EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(2);

  AutofillProfile full_profile(test::GetVerifiedProfile());
  CreditCard credit_card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  controller()->GetTestingManager()->AddTestingCreditCard(&credit_card);

  ui::MenuModel* model = controller()->MenuModelForSection(SECTION_BILLING);
  // Activate the "Add billing address" menu item.
  model->ActivatedAt(model->GetItemCount() - 2);

  // Fill in the inputs from the profile.
  FieldValueMap outputs;
  const DetailInputs& inputs =
      controller()->RequestedFieldsForSection(SECTION_BILLING);
  AutofillProfile full_profile2(test::GetVerifiedProfile2());
  for (size_t i = 0; i < inputs.size(); ++i) {
    const ServerFieldType type = inputs[i].type;
    outputs[type] = full_profile2.GetInfo(AutofillType(type), "en-US");
  }
  controller()->GetView()->SetUserInput(SECTION_BILLING, outputs);

  controller()->OnAccept();
  const AutofillProfile& added_profile =
      controller()->GetTestingManager()->imported_profile();

  const DetailInputs& shipping_inputs =
      controller()->RequestedFieldsForSection(SECTION_SHIPPING);
  for (size_t i = 0; i < shipping_inputs.size(); ++i) {
    const ServerFieldType type = shipping_inputs[i].type;
    EXPECT_EQ(full_profile2.GetInfo(AutofillType(type), "en-US"),
              added_profile.GetInfo(AutofillType(type), "en-US"));
  }
}

TEST_F(AutofillDialogControllerTest, VerifyCvv) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_));
  EXPECT_CALL(*controller()->GetTestingWalletClient(),
              AuthenticateInstrument(_, _));

  SubmitWithWalletItems(CompleteAndValidWalletItems());

  EXPECT_TRUE(NotificationsOfType(DialogNotification::REQUIRED_ACTION).empty());
  EXPECT_TRUE(controller()->SectionIsActive(SECTION_SHIPPING));
  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC_BILLING));
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));

  SuggestionState suggestion_state =
      controller()->SuggestionStateForSection(SECTION_CC_BILLING);
  EXPECT_TRUE(suggestion_state.extra_text.empty());

  controller()->OnDidGetFullWallet(
      wallet::GetTestFullWalletWithRequiredActions(
          std::vector<wallet::RequiredAction>(1, wallet::VERIFY_CVV)));
  ASSERT_TRUE(controller()->IsSubmitPausedOn(wallet::VERIFY_CVV));

  EXPECT_FALSE(
      NotificationsOfType(DialogNotification::REQUIRED_ACTION).empty());
  EXPECT_FALSE(controller()->SectionIsActive(SECTION_SHIPPING));
  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC_BILLING));

  suggestion_state =
      controller()->SuggestionStateForSection(SECTION_CC_BILLING);
  EXPECT_FALSE(suggestion_state.extra_text.empty());
  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC_BILLING));

  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));

  controller()->OnAccept();

  EXPECT_FALSE(controller()->GetDialogOverlay().image.IsEmpty());
}

TEST_F(AutofillDialogControllerTest, ErrorDuringSubmit) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_));

  SubmitWithWalletItems(CompleteAndValidWalletItems());

  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));

  controller()->OnWalletError(wallet::WalletClient::UNKNOWN_ERROR);

  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));
}

TEST_F(AutofillDialogControllerTest, ErrorDuringVerifyCvv) {
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_));

  SubmitWithWalletItems(CompleteAndValidWalletItems());
  controller()->OnDidGetFullWallet(
      wallet::GetTestFullWalletWithRequiredActions(
          std::vector<wallet::RequiredAction>(1, wallet::VERIFY_CVV)));

  ASSERT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  ASSERT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));

  controller()->OnWalletError(wallet::WalletClient::UNKNOWN_ERROR);

  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));
}

// Simulates receiving an INVALID_FORM_FIELD required action while processing a
// |WalletClientDelegate::OnDid{Save,Update}*()| call. This can happen if Online
// Wallet's server validation differs from Chrome's local validation.
TEST_F(AutofillDialogControllerTest, WalletServerSideValidation) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  controller()->OnAccept();

  std::vector<wallet::RequiredAction> required_actions;
  required_actions.push_back(wallet::INVALID_FORM_FIELD);

  std::vector<wallet::FormFieldError> form_errors;
  form_errors.push_back(
      wallet::FormFieldError(wallet::FormFieldError::INVALID_POSTAL_CODE,
                             wallet::FormFieldError::SHIPPING_ADDRESS));

  EXPECT_CALL(*controller()->GetView(), UpdateForErrors());
  controller()->OnDidSaveToWallet(std::string(),
                                  std::string(),
                                  required_actions,
                                  form_errors);
}

// Simulates receiving unrecoverable Wallet server validation errors.
TEST_F(AutofillDialogControllerTest, WalletServerSideValidationUnrecoverable) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  controller()->OnAccept();

  std::vector<wallet::RequiredAction> required_actions;
  required_actions.push_back(wallet::INVALID_FORM_FIELD);

  std::vector<wallet::FormFieldError> form_errors;
  form_errors.push_back(
      wallet::FormFieldError(wallet::FormFieldError::UNKNOWN_ERROR,
                             wallet::FormFieldError::UNKNOWN_LOCATION));

  controller()->OnDidSaveToWallet(std::string(),
                                  std::string(),
                                  required_actions,
                                  form_errors);

  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::REQUIRED_ACTION).size());
}

// Test Wallet banners are show in the right situations. These banners promote
// saving details into Wallet (i.e. "[x] Save details to Wallet").
TEST_F(AutofillDialogControllerTest, WalletBanners) {
  // Simulate non-signed-in case.
  SetUpControllerWithFormData(DefaultFormData());
  GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
  controller()->OnPassiveSigninFailure(error);
  EXPECT_EQ(0U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());

  // Sign in a user with a completed account.
  SetUpControllerWithFormData(DefaultFormData());
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());

  // Full account; should show "Details from Wallet" message.
  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());
  SwitchToAutofill();
  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());

  // Start over and sign in a user with an incomplete account.
  SetUpControllerWithFormData(DefaultFormData());
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // Partial account.
  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());

  SwitchToAutofill();
  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());

  // A Wallet error should kill any Wallet promos.
  controller()->OnWalletError(wallet::WalletClient::UNKNOWN_ERROR);

  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::WALLET_ERROR).size());
  EXPECT_EQ(0U, NotificationsOfType(
      DialogNotification::WALLET_USAGE_CONFIRMATION).size());
}

TEST_F(AutofillDialogControllerTest, ViewCancelDoesntSetPref) {
  ASSERT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));

  SwitchToAutofill();

  controller()->OnCancel();
  controller()->ViewClosed();

  EXPECT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
}

TEST_F(AutofillDialogControllerTest, SubmitWithSigninErrorDoesntSetPref) {
  ASSERT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));

  SimulateSigninError();
  FillCreditCardInputs();
  controller()->OnAccept();

  EXPECT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
}

// Tests that there's an overlay shown while waiting for full wallet items.
TEST_F(AutofillDialogControllerTest, WalletFirstRun) {
  EXPECT_TRUE(controller()->GetDialogOverlay().image.IsEmpty());

  SubmitWithWalletItems(CompleteAndValidWalletItems());
  EXPECT_FALSE(controller()->GetDialogOverlay().image.IsEmpty());

  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
  EXPECT_FALSE(controller()->GetDialogOverlay().image.IsEmpty());
  EXPECT_FALSE(form_structure());

  // Don't make the test wait for 2 seconds.
  controller()->ForceFinishSubmit();
  EXPECT_TRUE(form_structure());
}

TEST_F(AutofillDialogControllerTest, ViewSubmitSetsPref) {
  ASSERT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));

  SwitchToAutofill();
  FillCreditCardInputs();
  controller()->OnAccept();

  EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogPayWithoutWallet));

  // Try again with a signin error (just leaves the pref alone).
  SetUpControllerWithFormData(DefaultFormData());

  // Setting up the controller again should not change the pref.
  EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogPayWithoutWallet));

  SimulateSigninError();
  FillCreditCardInputs();
  controller()->OnAccept();
  EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogPayWithoutWallet));

  // Successfully choosing wallet does set the pref.
  // Note that OnDidGetWalletItems sets the account chooser to wallet mode.
  SetUpControllerWithFormData(DefaultFormData());

  controller()->OnDidFetchWalletCookieValue(std::string());
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  controller()->OnAccept();
  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
  controller()->ForceFinishSubmit();

  EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogPayWithoutWallet));
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogPayWithoutWallet));
}

TEST_F(AutofillDialogControllerTest, HideWalletEmail) {
  SwitchToAutofill();

  // Email field should be showing when using Autofill.
  EXPECT_TRUE(controller()->SectionIsActive(SECTION_BILLING));
  EXPECT_FALSE(controller()->SectionIsActive(SECTION_CC_BILLING));
  EXPECT_TRUE(SectionContainsField(SECTION_BILLING, EMAIL_ADDRESS));

  SwitchToWallet();

  // Reset the wallet state.
  controller()->OnDidGetWalletItems(scoped_ptr<wallet::WalletItems>());

  // Setup some wallet state, submit, and get a full wallet to end the flow.
  scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems();

  // Filling |form_structure()| depends on the current username and wallet items
  // being fetched. Until both of these have occurred, the user should not be
  // able to click Submit if using Wallet. The username fetch happened earlier.
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // Email field should be absent when using Wallet.
  EXPECT_FALSE(controller()->SectionIsActive(SECTION_BILLING));
  EXPECT_TRUE(controller()->SectionIsActive(SECTION_CC_BILLING));
  EXPECT_FALSE(SectionContainsField(SECTION_CC_BILLING, EMAIL_ADDRESS));

  controller()->OnAccept();
  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
  controller()->ForceFinishSubmit();

  ASSERT_TRUE(form_structure());
  size_t i = 0;
  for (; i < form_structure()->field_count(); ++i) {
    if (form_structure()->field(i)->Type().GetStorableType() == EMAIL_ADDRESS) {
      EXPECT_EQ(ASCIIToUTF16(kFakeEmail), form_structure()->field(i)->value);
      break;
    }
  }
  EXPECT_LT(i, form_structure()->field_count());
}

// Test if autofill types of returned form structure are correct for billing
// entries.
TEST_F(AutofillDialogControllerTest, AutofillTypes) {
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  controller()->OnAccept();
  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
  controller()->ForceFinishSubmit();
  ASSERT_TRUE(form_structure());
  ASSERT_EQ(20U, form_structure()->field_count());
  EXPECT_EQ(EMAIL_ADDRESS,
            form_structure()->field(0)->Type().GetStorableType());
  EXPECT_EQ(CREDIT_CARD_NUMBER,
            form_structure()->field(2)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(9)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_BILLING, form_structure()->field(9)->Type().group());
  EXPECT_EQ(ADDRESS_HOME_STATE,
            form_structure()->field(16)->Type().GetStorableType());
  EXPECT_EQ(ADDRESS_HOME, form_structure()->field(16)->Type().group());
}

TEST_F(AutofillDialogControllerTest, SaveDetailsInChrome) {
  SwitchToAutofill();
  EXPECT_CALL(*controller()->GetView(), ModelChanged()).Times(2);

  AutofillProfile full_profile(test::GetVerifiedProfile());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);

  CreditCard card(test::GetVerifiedCreditCard());
  controller()->GetTestingManager()->AddTestingCreditCard(&card);
  EXPECT_FALSE(controller()->ShouldOfferToSaveInChrome());

  controller()->MenuModelForSection(SECTION_BILLING)->ActivatedAt(0);
  EXPECT_FALSE(controller()->ShouldOfferToSaveInChrome());

  controller()->MenuModelForSection(SECTION_BILLING)->ActivatedAt(1);
  EXPECT_TRUE(controller()->ShouldOfferToSaveInChrome());

  profile()->ForceIncognito(true);
  EXPECT_FALSE(controller()->ShouldOfferToSaveInChrome());
}

// Tests that user is prompted when using instrument with minimal address.
TEST_F(AutofillDialogControllerTest, UpgradeMinimalAddress) {
  // A minimal address being selected should trigger error validation in the
  // view. Called once for each incomplete suggestion.
  EXPECT_CALL(*controller()->GetView(), UpdateForErrors());

  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentWithIdAndAddress(
      "id", wallet::GetTestMinimalAddress()));
  scoped_ptr<wallet::Address> address(wallet::GetTestShippingAddress());
  address->set_is_complete_address(false);
  wallet_items->AddAddress(address.Pass());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // Assert that dialog's SECTION_CC_BILLING section is in edit mode.
  ASSERT_TRUE(controller()->IsEditingExistingData(SECTION_CC_BILLING));
  // Shipping section should be in edit mode because of
  // is_minimal_shipping_address.
  ASSERT_TRUE(controller()->IsEditingExistingData(SECTION_SHIPPING));
}

TEST_F(AutofillDialogControllerTest, RiskNeverLoadsWithPendingLegalDocuments) {
  EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(0);

  scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems();
  wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
  wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  controller()->OnAccept();
}

TEST_F(AutofillDialogControllerTest, RiskLoadsAfterAcceptingLegalDocuments) {
  EXPECT_CALL(*controller(), LoadRiskFingerprintData()).Times(0);

  scoped_ptr<wallet::WalletItems> wallet_items = CompleteAndValidWalletItems();
  wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
  wallet_items->AddLegalDocument(wallet::GetTestLegalDocument());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  testing::Mock::VerifyAndClear(controller());
  EXPECT_CALL(*controller(), LoadRiskFingerprintData());

  controller()->OnAccept();

  // Simulate a risk load and verify |GetRiskData()| matches the encoded value.
  controller()->OnDidAcceptLegalDocuments();
  controller()->OnDidLoadRiskFingerprintData(GetFakeFingerprint().Pass());
  EXPECT_EQ(kFakeFingerprintEncoded, controller()->GetRiskData());
}

TEST_F(AutofillDialogControllerTest, NoManageMenuItemForNewWalletUsers) {
  // Make sure the menu model item is created for a returning Wallet user.
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  EXPECT_TRUE(controller()->MenuModelForSection(SECTION_CC_BILLING));
  // "Same as billing", "123 address", "Add address...", and "Manage addresses".
  EXPECT_EQ(
      4, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());

  // Make sure the menu model item is not created for new Wallet users.
  base::DictionaryValue dict;
  scoped_ptr<base::ListValue> required_actions(new base::ListValue);
  required_actions->AppendString("setup_wallet");
  dict.Set("required_action", required_actions.release());
  controller()->OnDidGetWalletItems(
      wallet::WalletItems::CreateWalletItems(dict).Pass());

  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC_BILLING));
  // "Same as billing" and "Add address...".
  EXPECT_EQ(
      2, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, ShippingSectionCanBeHidden) {
  FormFieldData email_field;
  email_field.autocomplete_attribute = "email";
  FormFieldData cc_field;
  cc_field.autocomplete_attribute = "cc-number";
  FormFieldData billing_field;
  billing_field.autocomplete_attribute = "billing region";

  FormData form_data;
  form_data.fields.push_back(email_field);
  form_data.fields.push_back(cc_field);
  form_data.fields.push_back(billing_field);

  AutofillProfile full_profile(test::GetVerifiedProfile());
  controller()->GetTestingManager()->AddTestingProfile(&full_profile);
  SetUpControllerWithFormData(form_data);

  SwitchToAutofill();

  EXPECT_FALSE(controller()->SectionIsActive(SECTION_SHIPPING));

  FillCreditCardInputs();
  controller()->OnAccept();
  EXPECT_TRUE(form_structure());
}

TEST_F(AutofillDialogControllerTest, ShippingSectionCanBeHiddenForWallet) {
  FormFieldData email_field;
  email_field.autocomplete_attribute = "email";
  FormFieldData cc_field;
  cc_field.autocomplete_attribute = "cc-number";
  FormFieldData billing_field;
  billing_field.autocomplete_attribute = "billing region";

  FormData form_data;
  form_data.fields.push_back(email_field);
  form_data.fields.push_back(cc_field);
  form_data.fields.push_back(billing_field);

  SetUpControllerWithFormData(form_data);
  EXPECT_FALSE(controller()->SectionIsActive(SECTION_SHIPPING));
  EXPECT_FALSE(controller()->IsShippingAddressRequired());

  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetFullWallet(_));
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  SubmitWithWalletItems(wallet_items.Pass());
  controller()->OnDidGetFullWallet(wallet::GetTestFullWalletInstrumentOnly());
  controller()->ForceFinishSubmit();
  EXPECT_TRUE(form_structure());
}

TEST_F(AutofillDialogControllerTest, NotProdNotification) {
  // To make IsPayingWithWallet() true.
  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED));

  CommandLine* command_line = CommandLine::ForCurrentProcess();
  ASSERT_EQ(
      "",
      command_line->GetSwitchValueASCII(switches::kWalletServiceUseSandbox));

  // Default everywhere is to use prod (no warning).
  EXPECT_EQ(0U,
            NotificationsOfType(DialogNotification::DEVELOPER_WARNING).size());

  command_line->AppendSwitchASCII(switches::kWalletServiceUseSandbox, "1");
  EXPECT_EQ(1U,
            NotificationsOfType(DialogNotification::DEVELOPER_WARNING).size());
}

// Ensure Wallet instruments marked expired by the server are shown as invalid.
TEST_F(AutofillDialogControllerTest, WalletExpiredCard) {
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentExpired());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC_BILLING));

  const DetailInputs& inputs =
      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
  FieldValueMap outputs;
  CopyInitialValues(inputs, &outputs);

  // The local inputs are invalid because the server said so. They'll
  // stay invalid until they differ from the remotely fetched model.
  ValidityMessages messages = controller()->InputsAreValid(SECTION_CC_BILLING,
                                                           outputs);
  EXPECT_TRUE(messages.HasSureError(CREDIT_CARD_EXP_MONTH));
  EXPECT_TRUE(messages.HasSureError(CREDIT_CARD_EXP_4_DIGIT_YEAR));

  // Make the local input year differ from the instrument.
  CopyInitialValues(inputs, &outputs);
  outputs[CREDIT_CARD_EXP_4_DIGIT_YEAR] = ASCIIToUTF16("3002");
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, outputs);
  EXPECT_FALSE(HasAnyError(messages, CREDIT_CARD_EXP_MONTH));
  EXPECT_FALSE(HasAnyError(messages, CREDIT_CARD_EXP_4_DIGIT_YEAR));

  // Make the local input month differ from the instrument.
  CopyInitialValues(inputs, &outputs);
  outputs[CREDIT_CARD_EXP_MONTH] = ASCIIToUTF16("06");
  messages = controller()->InputsAreValid(SECTION_CC_BILLING, outputs);
  EXPECT_FALSE(HasAnyError(messages, CREDIT_CARD_EXP_MONTH));
  EXPECT_FALSE(HasAnyError(messages, CREDIT_CARD_EXP_4_DIGIT_YEAR));
}

TEST_F(AutofillDialogControllerTest, ChooseAnotherInstrumentOrAddress) {
  SubmitWithWalletItems(CompleteAndValidWalletItems());

  EXPECT_EQ(0U, NotificationsOfType(
      DialogNotification::REQUIRED_ACTION).size());
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->OnDidGetFullWallet(
      wallet::GetTestFullWalletWithRequiredActions(
          std::vector<wallet::RequiredAction>(
              1, wallet::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS)));
  EXPECT_EQ(1U, NotificationsOfType(
      DialogNotification::REQUIRED_ACTION).size());
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());

  controller()->OnAccept();
  EXPECT_EQ(0U, NotificationsOfType(
      DialogNotification::REQUIRED_ACTION).size());
}

TEST_F(AutofillDialogControllerTest, NewCardBubbleShown) {
  SwitchToAutofill();
  FillCreditCardInputs();
  controller()->OnAccept();
  controller()->ViewClosed();

  EXPECT_EQ(1, mock_new_card_bubble_controller()->bubbles_shown());
  EXPECT_EQ(0, test_generated_bubble_controller()->bubbles_shown());
}

TEST_F(AutofillDialogControllerTest, GeneratedCardBubbleShown) {
  SubmitWithWalletItems(CompleteAndValidWalletItems());
  controller()->OnDidGetFullWallet(wallet::GetTestFullWallet());
  controller()->ForceFinishSubmit();
  controller()->ViewClosed();

  EXPECT_EQ(0, mock_new_card_bubble_controller()->bubbles_shown());
  EXPECT_EQ(1, test_generated_bubble_controller()->bubbles_shown());
}

// Verify that new Wallet data is fetched when the user switches away from the
// tab hosting the Autofill dialog and back. Also verify that the user's
// selection is preserved across this re-fetch.
TEST_F(AutofillDialogControllerTest, ReloadWalletItemsOnActivation) {
  // Initialize some Wallet data.
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // Initially, the default entries should be selected.
  ui::MenuModel* cc_billing_model =
      controller()->MenuModelForSection(SECTION_CC_BILLING);
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  // "add", "manage", and 2 suggestions.
  ASSERT_EQ(4, cc_billing_model->GetItemCount());
  EXPECT_TRUE(cc_billing_model->IsItemCheckedAt(0));
  // "use billing", "add", "manage", and 2 suggestions.
  ASSERT_EQ(5, shipping_model->GetItemCount());
  EXPECT_TRUE(shipping_model->IsItemCheckedAt(2));

  // Select entries other than the defaults.
  cc_billing_model->ActivatedAt(1);
  shipping_model->ActivatedAt(1);
  // 2 suggestions, "add", and "manage".
  ASSERT_EQ(4, cc_billing_model->GetItemCount());
  EXPECT_TRUE(cc_billing_model->IsItemCheckedAt(1));
  // "use billing", 2 suggestions, "add", "manage".
  ASSERT_EQ(5, shipping_model->GetItemCount());
  EXPECT_TRUE(shipping_model-> IsItemCheckedAt(1));

  // Simulate switching away from the tab and back.  This should issue a request
  // for wallet items.
  controller()->ClearLastWalletItemsFetchTimestampForTesting();
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->TabActivated();

  // Simulate a response that includes different items.
  wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrumentExpired());
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // The previously selected entries should still be selected.
  // 3 suggestions, "add", and "manage".
  ASSERT_EQ(5, cc_billing_model->GetItemCount());
  EXPECT_TRUE(cc_billing_model->IsItemCheckedAt(2));
  // "use billing", 1 suggestion, "add", and "manage".
  ASSERT_EQ(4, shipping_model->GetItemCount());
  EXPECT_TRUE(shipping_model->IsItemCheckedAt(1));
}

// Verify that if the default values change when re-fetching Wallet data, these
// new default values are selected in the dialog.
TEST_F(AutofillDialogControllerTest,
       ReloadWalletItemsOnActivationWithNewDefaults) {
  // Initialize some Wallet data.
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddInstrument(wallet::GetTestMaskedInstrument());
  wallet_items->AddInstrument(wallet::GetTestNonDefaultMaskedInstrument());
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // Initially, the default entries should be selected.
  ui::MenuModel* cc_billing_model =
      controller()->MenuModelForSection(SECTION_CC_BILLING);
  ui::MenuModel* shipping_model =
      controller()->MenuModelForSection(SECTION_SHIPPING);
  // 2 suggestions, "add", and "manage".
  ASSERT_EQ(4, cc_billing_model->GetItemCount());
  EXPECT_TRUE(cc_billing_model->IsItemCheckedAt(0));
  // "use billing", 2 suggestions, "add", and "manage".
  ASSERT_EQ(5, shipping_model->GetItemCount());
  EXPECT_TRUE(shipping_model->IsItemCheckedAt(2));

  // Simulate switching away from the tab and back.  This should issue a request
  // for wallet items.
  controller()->ClearLastWalletItemsFetchTimestampForTesting();
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->TabActivated();

  // Simulate a response that includes different default values.
  wallet_items =
      wallet::GetTestWalletItemsWithDefaultIds("new_default_instrument_id",
                                               "new_default_address_id",
                                               wallet::AMEX_DISALLOWED);
  scoped_ptr<wallet::Address> other_address = wallet::GetTestShippingAddress();
  other_address->set_object_id("other_address_id");
  scoped_ptr<wallet::Address> new_default_address =
      wallet::GetTestNonDefaultShippingAddress();
  new_default_address->set_object_id("new_default_address_id");

  wallet_items->AddInstrument(
      wallet::GetTestMaskedInstrumentWithId("other_instrument_id"));
  wallet_items->AddInstrument(
      wallet::GetTestMaskedInstrumentWithId("new_default_instrument_id"));
  wallet_items->AddAddress(new_default_address.Pass());
  wallet_items->AddAddress(other_address.Pass());
  controller()->OnDidGetWalletItems(wallet_items.Pass());

  // The new default entries should be selected.
  // 2 suggestions, "add", and "manage".
  ASSERT_EQ(4, cc_billing_model->GetItemCount());
  EXPECT_TRUE(cc_billing_model->IsItemCheckedAt(1));
  // "use billing", 2 suggestions, "add", and "manage".
  ASSERT_EQ(5, shipping_model->GetItemCount());
  EXPECT_TRUE(shipping_model->IsItemCheckedAt(1));
}

TEST_F(AutofillDialogControllerTest, ReloadWithEmptyWalletItems) {
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  controller()->MenuModelForSection(SECTION_CC_BILLING)->ActivatedAt(1);
  controller()->MenuModelForSection(SECTION_SHIPPING)->ActivatedAt(1);

  controller()->ClearLastWalletItemsFetchTimestampForTesting();
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->TabActivated();

  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED));

  EXPECT_FALSE(controller()->MenuModelForSection(SECTION_CC_BILLING));
  EXPECT_EQ(
      3, controller()->MenuModelForSection(SECTION_SHIPPING)->GetItemCount());
}

TEST_F(AutofillDialogControllerTest, SaveInChromeByDefault) {
  EXPECT_TRUE(controller()->ShouldSaveInChrome());
  SwitchToAutofill();
  FillCreditCardInputs();
  controller()->OnAccept();
  EXPECT_TRUE(controller()->ShouldSaveInChrome());
}

TEST_F(AutofillDialogControllerTest,
       SaveInChromePreferenceNotRememberedOnCancel) {
  EXPECT_TRUE(controller()->ShouldSaveInChrome());
  SwitchToAutofill();
  controller()->GetView()->CheckSaveDetailsLocallyCheckbox(false);
  controller()->OnCancel();
  EXPECT_TRUE(controller()->ShouldSaveInChrome());
}

TEST_F(AutofillDialogControllerTest,
       SaveInChromePreferenceRememberedOnSuccess) {
  EXPECT_TRUE(controller()->ShouldSaveInChrome());
  SwitchToAutofill();
  FillCreditCardInputs();
  controller()->GetView()->CheckSaveDetailsLocallyCheckbox(false);
  controller()->OnAccept();
  EXPECT_FALSE(controller()->ShouldSaveInChrome());
}

TEST_F(AutofillDialogControllerTest,
       SubmitButtonIsDisabled_SpinnerFinishesBeforeDelay) {
  // Reset Wallet state.
  controller()->OnDidGetWalletItems(scoped_ptr<wallet::WalletItems>());

  EXPECT_EQ(1, controller()->get_submit_button_delay_count());

  // Begin the submit button delay.
  controller()->SimulateSubmitButtonDelayBegin();

  EXPECT_TRUE(controller()->ShouldShowSpinner());
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // Stop the spinner.
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());

  EXPECT_FALSE(controller()->ShouldShowSpinner());
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // End the submit button delay.
  controller()->SimulateSubmitButtonDelayEnd();

  EXPECT_FALSE(controller()->ShouldShowSpinner());
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
}

TEST_F(AutofillDialogControllerTest,
       SubmitButtonIsDisabled_SpinnerFinishesAfterDelay) {
  // Reset Wallet state.
  controller()->OnDidGetWalletItems(scoped_ptr<wallet::WalletItems>());

  EXPECT_EQ(1, controller()->get_submit_button_delay_count());

  // Begin the submit button delay.
  controller()->SimulateSubmitButtonDelayBegin();

  EXPECT_TRUE(controller()->ShouldShowSpinner());
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // End the submit button delay.
  controller()->SimulateSubmitButtonDelayEnd();

  EXPECT_TRUE(controller()->ShouldShowSpinner());
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // Stop the spinner.
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());

  EXPECT_FALSE(controller()->ShouldShowSpinner());
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
}

TEST_F(AutofillDialogControllerTest, SubmitButtonIsDisabled_NoSpinner) {
  SwitchToAutofill();

  EXPECT_EQ(1, controller()->get_submit_button_delay_count());

  // Begin the submit button delay.
  controller()->SimulateSubmitButtonDelayBegin();

  EXPECT_FALSE(controller()->ShouldShowSpinner());
  EXPECT_FALSE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));

  // End the submit button delay.
  controller()->SimulateSubmitButtonDelayEnd();

  EXPECT_FALSE(controller()->ShouldShowSpinner());
  EXPECT_TRUE(controller()->IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
}

TEST_F(AutofillDialogControllerTest, IconsForFields_NoCreditCard) {
  FieldValueMap values;
  values[EMAIL_ADDRESS] = ASCIIToUTF16(kFakeEmail);
  FieldIconMap icons = controller()->IconsForFields(values);
  EXPECT_TRUE(icons.empty());
}

TEST_F(AutofillDialogControllerTest, IconsForFields_CreditCardNumberOnly) {
  FieldValueMap values;
  values[EMAIL_ADDRESS] = ASCIIToUTF16(kFakeEmail);
  values[CREDIT_CARD_NUMBER] = ASCIIToUTF16(kTestCCNumberVisa);
  FieldIconMap icons = controller()->IconsForFields(values);
  EXPECT_EQ(1UL, icons.size());
  EXPECT_EQ(1UL, icons.count(CREDIT_CARD_NUMBER));
}

TEST_F(AutofillDialogControllerTest, IconsForFields_CvcOnly) {
  FieldValueMap values;
  values[EMAIL_ADDRESS] = ASCIIToUTF16(kFakeEmail);
  values[CREDIT_CARD_VERIFICATION_CODE] = ASCIIToUTF16("123");
  FieldIconMap icons = controller()->IconsForFields(values);
  EXPECT_EQ(1UL, icons.size());
  EXPECT_EQ(1UL, icons.count(CREDIT_CARD_VERIFICATION_CODE));
}

TEST_F(AutofillDialogControllerTest, IconsForFields_BothCreditCardAndCvc) {
  FieldValueMap values;
  values[EMAIL_ADDRESS] = ASCIIToUTF16(kFakeEmail);
  values[CREDIT_CARD_NUMBER] = ASCIIToUTF16(kTestCCNumberVisa);
  values[CREDIT_CARD_VERIFICATION_CODE] = ASCIIToUTF16("123");
  FieldIconMap icons = controller()->IconsForFields(values);
  EXPECT_EQ(2UL, icons.size());
  EXPECT_EQ(1UL, icons.count(CREDIT_CARD_VERIFICATION_CODE));
  EXPECT_EQ(1UL, icons.count(CREDIT_CARD_NUMBER));
}

TEST_F(AutofillDialogControllerTest, FieldControlsIcons) {
  EXPECT_TRUE(controller()->FieldControlsIcons(CREDIT_CARD_NUMBER));
  EXPECT_FALSE(controller()->FieldControlsIcons(CREDIT_CARD_VERIFICATION_CODE));
  EXPECT_FALSE(controller()->FieldControlsIcons(EMAIL_ADDRESS));
}

TEST_F(AutofillDialogControllerTest, SaveCreditCardIncludesName_NoBilling) {
  SwitchToAutofill();

  CreditCard test_credit_card(test::GetVerifiedCreditCard());
  FillInputs(SECTION_CC, test_credit_card);

  AutofillProfile test_profile(test::GetVerifiedProfile());
  FillInputs(SECTION_BILLING, test_profile);

  UseBillingForShipping();

  controller()->GetView()->CheckSaveDetailsLocallyCheckbox(true);
  controller()->OnAccept();

  TestPersonalDataManager* test_pdm = controller()->GetTestingManager();
  const CreditCard& imported_card = test_pdm->imported_credit_card();
  EXPECT_EQ(test_profile.GetRawInfo(NAME_FULL),
            imported_card.GetRawInfo(CREDIT_CARD_NAME));
}

TEST_F(AutofillDialogControllerTest, SaveCreditCardIncludesName_WithBilling) {
  SwitchToAutofill();

  TestPersonalDataManager* test_pdm = controller()->GetTestingManager();
  AutofillProfile test_profile(test::GetVerifiedProfile());

  EXPECT_CALL(*controller()->GetView(), ModelChanged());
  test_pdm->AddTestingProfile(&test_profile);
  ASSERT_TRUE(controller()->MenuModelForSection(SECTION_BILLING));

  CreditCard test_credit_card(test::GetVerifiedCreditCard());
  FillInputs(SECTION_CC, test_credit_card);

  controller()->GetView()->CheckSaveDetailsLocallyCheckbox(true);
  controller()->OnAccept();

  const CreditCard& imported_card = test_pdm->imported_credit_card();
  EXPECT_EQ(test_profile.GetRawInfo(NAME_FULL),
            imported_card.GetRawInfo(CREDIT_CARD_NAME));

  controller()->ViewClosed();
}

TEST_F(AutofillDialogControllerTest, InputEditability) {
  // Empty wallet items: all fields are editable.
  scoped_ptr<wallet::WalletItems> items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  controller()->OnDidGetWalletItems(items.Pass());

  DialogSection sections[] = { SECTION_CC_BILLING, SECTION_SHIPPING };
  for (size_t i = 0; i < arraysize(sections); ++i) {
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(sections[i]);
    for (size_t j = 0; j < inputs.size(); ++j) {
      EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i]));
    }
  }

  // Expired instrument: CC number + CVV are not editable.
  items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  scoped_ptr<wallet::WalletItems::MaskedInstrument> expired_instrument =
      wallet::GetTestMaskedInstrumentExpired();
  items->AddInstrument(expired_instrument.Pass());
  controller()->OnDidGetWalletItems(items.Pass());
  EXPECT_TRUE(controller()->IsEditingExistingData(SECTION_CC_BILLING));

  const DetailInputs& inputs =
      controller()->RequestedFieldsForSection(SECTION_CC_BILLING);
  FieldValueMap outputs;
  CopyInitialValues(inputs, &outputs);
  controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs);

  for (size_t i = 0; i < arraysize(sections); ++i) {
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(sections[i]);
    for (size_t j = 0; j < inputs.size(); ++j) {
      if (inputs[j].type == CREDIT_CARD_NUMBER ||
          inputs[j].type == CREDIT_CARD_VERIFICATION_CODE) {
        EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i]));
      } else {
        EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i]));
      }
    }
  }

  // User changes the billing address; same story.
  outputs[ADDRESS_BILLING_ZIP] = ASCIIToUTF16("77025");
  controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs);
  for (size_t i = 0; i < arraysize(sections); ++i) {
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(sections[i]);
    for (size_t j = 0; j < inputs.size(); ++j) {
      if (inputs[j].type == CREDIT_CARD_NUMBER ||
          inputs[j].type == CREDIT_CARD_VERIFICATION_CODE) {
        EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i]));
      } else {
        EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i]));
      }
    }
  }

  // User changes a detail of the CC itself (expiration date), CVV is now
  // editable (and mandatory).
  outputs[CREDIT_CARD_EXP_MONTH] = ASCIIToUTF16("06");
  controller()->GetView()->SetUserInput(SECTION_CC_BILLING, outputs);
  for (size_t i = 0; i < arraysize(sections); ++i) {
    const DetailInputs& inputs =
        controller()->RequestedFieldsForSection(sections[i]);
    for (size_t j = 0; j < inputs.size(); ++j) {
      if (inputs[j].type == CREDIT_CARD_NUMBER)
        EXPECT_FALSE(controller()->InputIsEditable(inputs[j], sections[i]));
      else
        EXPECT_TRUE(controller()->InputIsEditable(inputs[j], sections[i]));
    }
  }
}

// When the default country is something besides US, wallet is not selected
// and the account chooser shouldn't be visible.
// TODO(estade): this is disabled until http://crbug.com/323641 is fixed.
TEST_F(AutofillDialogControllerTest, DISABLED_HideWalletInOtherCountries) {
  ResetControllerWithFormData(DefaultFormData());
  controller()->GetTestingManager()->set_default_country_code("US");
  controller()->Show();
  EXPECT_TRUE(
      controller()->AccountChooserModelForTesting()->WalletIsSelected());
  controller()->OnDidFetchWalletCookieValue(std::string());
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  EXPECT_TRUE(controller()->ShouldShowAccountChooser());
  EXPECT_TRUE(
      controller()->AccountChooserModelForTesting()->WalletIsSelected());

  ResetControllerWithFormData(DefaultFormData());
  controller()->GetTestingManager()->set_default_country_code("ES");
  controller()->Show();
  EXPECT_FALSE(controller()->ShouldShowAccountChooser());
  EXPECT_FALSE(
      controller()->AccountChooserModelForTesting()->WalletIsSelected());
}

TEST_F(AutofillDialogControllerTest, DontGetWalletTillNecessary) {
  // When starting on local data mode, the dialog will provide a "Use Google
  // Wallet" link.
  profile()->GetPrefs()->SetBoolean(
      ::prefs::kAutofillDialogPayWithoutWallet, true);
  ResetControllerWithFormData(DefaultFormData());
  controller()->Show();
  base::string16 use_wallet_text = controller()->SignInLinkText();
  EXPECT_EQ(TestAutofillDialogController::NOT_CHECKED,
            controller()->SignedInState());

  // When clicked, this link will ask for wallet items. If there's a signin
  // failure, the link will switch to "Sign in to use Google Wallet".
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->SignInLinkClicked();
  EXPECT_NE(TestAutofillDialogController::NOT_CHECKED,
            controller()->SignedInState());
  controller()->OnDidFetchWalletCookieValue(std::string());
  controller()->OnDidGetWalletItems(CompleteAndValidWalletItems());
  controller()->OnPassiveSigninFailure(GoogleServiceAuthError(
      GoogleServiceAuthError::CONNECTION_FAILED));
  EXPECT_NE(use_wallet_text, controller()->SignInLinkText());
}

TEST_F(AutofillDialogControllerTest, MultiAccountSwitch) {
  std::vector<std::string> users;
  users.push_back("user_1@example.com");
  users.push_back("user_2@example.com");
  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItemsWithUsers(users, 0));

  // Items should be: Account 1, account 2, add account, disable wallet.
  EXPECT_EQ(4, controller()->MenuModelForAccountChooser()->GetItemCount());
  EXPECT_EQ(0U, controller()->GetTestingWalletClient()->user_index());

  // GetWalletItems should be called when the user switches accounts.
  EXPECT_CALL(*controller()->GetTestingWalletClient(), GetWalletItems());
  controller()->MenuModelForAccountChooser()->ActivatedAt(1);
  // The wallet client should be updated to the new user index.
  EXPECT_EQ(1U, controller()->GetTestingWalletClient()->user_index());
}

TEST_F(AutofillDialogControllerTest, PassiveAuthFailure) {
  controller()->OnDidGetWalletItems(
      wallet::GetTestWalletItemsWithRequiredAction(
           wallet::PASSIVE_GAIA_AUTH));
  EXPECT_TRUE(controller()->ShouldShowSpinner());
  controller()->OnPassiveSigninFailure(GoogleServiceAuthError(
      GoogleServiceAuthError::NONE));
  EXPECT_FALSE(controller()->ShouldShowSpinner());
}

TEST_F(AutofillDialogControllerTest, WalletShippingSameAsBilling) {
  // Assert initial state.
  ASSERT_FALSE(profile()->GetPrefs()->HasPrefPath(
      ::prefs::kAutofillDialogWalletShippingSameAsBilling));

  // Verify that false pref defaults to wallet defaults.
  scoped_ptr<wallet::WalletItems> wallet_items =
      wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  ASSERT_FALSE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogWalletShippingSameAsBilling));
  EXPECT_EQ(2, GetMenuModelForSection(SECTION_SHIPPING)->checked_item());

  // Set "Same as Billing" for the shipping address and verify it sets the pref
  // and selects the appropriate menu item.
  UseBillingForShipping();
  ASSERT_EQ(0, GetMenuModelForSection(SECTION_SHIPPING)->checked_item());
  controller()->ForceFinishSubmit();
  ASSERT_TRUE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogWalletShippingSameAsBilling));

  // Getting new wallet info shouldn't disrupt the preference and menu should be
  // set accordingly.
  Reset();
  wallet_items = wallet::GetTestWalletItems(wallet::AMEX_DISALLOWED);
  wallet_items->AddAddress(wallet::GetTestNonDefaultShippingAddress());
  wallet_items->AddAddress(wallet::GetTestShippingAddress());
  controller()->OnDidGetWalletItems(wallet_items.Pass());
  EXPECT_TRUE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogWalletShippingSameAsBilling));
  EXPECT_EQ(0, GetMenuModelForSection(SECTION_SHIPPING)->checked_item());

  // Choose a different address and ensure pref gets set to false.
  controller()->MenuModelForSection(SECTION_SHIPPING)->ActivatedAt(1);
  controller()->ForceFinishSubmit();
  EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(
      ::prefs::kAutofillDialogWalletShippingSameAsBilling));
}

// Verifies that a call to the IconsForFields() method before the card type is
// known returns a placeholder image that is at least as large as the icons for
// all of the supported major credit card issuers.
TEST_F(AutofillDialogControllerTest, IconReservedForCreditCardField) {
  FieldValueMap inputs;
  inputs[CREDIT_CARD_NUMBER] = base::string16();

  FieldIconMap icons = controller()->IconsForFields(inputs);
  EXPECT_EQ(1U, icons.size());

  ASSERT_EQ(1U, icons.count(CREDIT_CARD_NUMBER));
  gfx::Image placeholder_icon = icons[CREDIT_CARD_NUMBER];

  // Verify that the placeholder icon is at least as large as the icons for the
  // supported credit card issuers.
  const int kSupportedCardIdrs[] = {
    IDR_AUTOFILL_CC_AMEX,
    IDR_AUTOFILL_CC_DINERS,
    IDR_AUTOFILL_CC_DISCOVER,
    IDR_AUTOFILL_CC_GENERIC,
    IDR_AUTOFILL_CC_JCB,
    IDR_AUTOFILL_CC_MASTERCARD,
    IDR_AUTOFILL_CC_VISA,
  };
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  for (size_t i = 0; i < arraysize(kSupportedCardIdrs); ++i) {
    SCOPED_TRACE(base::IntToString(i));
    gfx::Image supported_card_icon = rb.GetImageNamed(kSupportedCardIdrs[i]);
    EXPECT_GE(placeholder_icon.Width(), supported_card_icon.Width());
    EXPECT_GE(placeholder_icon.Height(), supported_card_icon.Height());
  }
}

}  // namespace autofill