chromium/components/autofill/ios/browser/autofill_util_unittests.mm

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

#import "components/autofill/ios/browser/autofill_util.h"

#import "base/memory/scoped_refptr.h"
#import "base/strings/utf_string_conversions.h"
#import "base/values.h"
#import "components/autofill/core/common/field_data_manager.h"
#import "components/autofill/core/common/form_data.h"
#import "components/autofill/core/common/form_field_data.h"
#import "components/autofill/core/common/unique_ids.h"
#import "testing/platform_test.h"

using AutofillUtilTest = PlatformTest;

using autofill::ExtractIDs;
using autofill::ExtractFillingResults;
using autofill::FieldRendererId;
using base::ASCIIToUTF16;

TEST_F(AutofillUtilTest, ExtractIDs) {
  NSString* valid_ids = @"[\"1\",\"2\"]";
  std::set<FieldRendererId> expected_result = {FieldRendererId(1),
                                               FieldRendererId(2)};
  std::optional<std::set<FieldRendererId>> extracted_ids =
      ExtractIDs<FieldRendererId>(valid_ids);
  EXPECT_TRUE(extracted_ids);
  EXPECT_EQ(expected_result, *extracted_ids);

  NSString* empty_ids = @"[]";
  extracted_ids = ExtractIDs<FieldRendererId>(empty_ids);
  EXPECT_TRUE(extracted_ids);
  EXPECT_TRUE(extracted_ids.value().empty());

  NSString* invalid_ids1 = @"[\"1\"\"2\"]";
  EXPECT_FALSE(ExtractIDs<FieldRendererId>(invalid_ids1));
  NSString* invalid_ids2 = @"[1,2]";
  EXPECT_FALSE(ExtractIDs<FieldRendererId>(invalid_ids2));
  NSString* too_big_id = @"[\"111222333444\"]";
  EXPECT_FALSE(ExtractIDs<FieldRendererId>(too_big_id));
}

TEST_F(AutofillUtilTest, ExtractFillingResults) {
  std::map<uint32_t, std::u16string> extracted_results;
  NSString* valid_results = @"{\"1\":\"username\",\"2\":\"adress\"}";
  std::map<uint32_t, std::u16string> expected_result = {{1, u"username"},
                                                        {2, u"adress"}};
  EXPECT_TRUE(ExtractFillingResults(valid_results, &extracted_results));
  EXPECT_EQ(expected_result, extracted_results);

  extracted_results.clear();
  NSString* empty_results = @"{}";
  EXPECT_TRUE(ExtractFillingResults(empty_results, &extracted_results));
  EXPECT_TRUE(extracted_results.empty());

  NSString* invalid_results1 = @"{\"1\":\"username\"\"2\":\"adress\"}";
  EXPECT_FALSE(ExtractFillingResults(invalid_results1, &extracted_results));
  NSString* invalid_results2 = @"{\"1\":\"username\"\"2\":100}";
  EXPECT_FALSE(ExtractFillingResults(invalid_results2, &extracted_results));
}

// Test that the properties mask is extracted from the form field data.
TEST_F(AutofillUtilTest, ExtractFormFieldData_PropertiesMask) {
  base::Value::Dict field;
  // Set mandatory field attributes.
  field.Set("name", base::Value("email"));
  field.Set("form_control_type", base::Value("text"));

  // Set field attribute to get mask.
  field.Set("renderer_id", base::Value("1"));

  const scoped_refptr<autofill::FieldDataManager> field_data_manager =
      base::MakeRefCounted<autofill::FieldDataManager>();
  // Set test field property as user typed.
  field_data_manager->UpdateFieldDataMap(
      autofill::FieldRendererId(1), u"my@mail",
      autofill::FieldPropertiesFlags::kUserTyped);

  autofill::FormFieldData field_data;
  autofill::ExtractFormFieldData(field, *field_data_manager, &field_data);

  EXPECT_EQ(u"my@mail", field_data.user_input());
  EXPECT_EQ(autofill::FieldPropertiesFlags::kUserTyped,
            field_data.properties_mask());
}

// Tests various aspects of converting hex IDs equivalent to those generated by
// JavaScript into UnguessableTokens.
TEST_F(AutofillUtilTest, DeserializeTokens) {
  // Should work with a 32-character (128-bit) hex string. Also test that
  // hex conversion is robust to upper/lower case.
  auto token = autofill::DeserializeJavaScriptFrameId(
      "0123456789abcdef0123456789ABCDEF");
  ASSERT_TRUE(token.has_value());
  EXPECT_EQ("0123456789abcdef0123456789abcdef",
            base::ToLowerASCII(token->ToString()));

  // Should fail if the string has the wrong length
  token = autofill::DeserializeJavaScriptFrameId(std::string(4, '1'));
  EXPECT_FALSE(token.has_value());
  token = autofill::DeserializeJavaScriptFrameId(std::string(34, 'f'));
  EXPECT_FALSE(token.has_value());

  // Should fail if the string isn't hex
  token = autofill::DeserializeJavaScriptFrameId(std::string(32, '?'));
  EXPECT_FALSE(token.has_value());
}

// Test that the properties mask is extracted from the form field data.
TEST_F(AutofillUtilTest, ExtractRemoteFrameToken) {
  base::Value::Dict remote_frame_token_dict;
  remote_frame_token_dict.Set("token",
                              base::Value("beefbeefbeefbeefcafecafecafecafe"));
  remote_frame_token_dict.Set("predecessor", base::Value(64));

  autofill::FrameTokenWithPredecessor token_with_predecessor;

  ASSERT_TRUE(ExtractRemoteFrameToken(remote_frame_token_dict,
                                      &token_with_predecessor));
  EXPECT_EQ(base::ToLowerASCII(absl::get<autofill::RemoteFrameToken>(
                                   token_with_predecessor.token)
                                   .ToString()),
            "beefbeefbeefbeefcafecafecafecafe");
  EXPECT_EQ(token_with_predecessor.predecessor, 64);

  base::Value::Dict malformed1;
  malformed1.Set("garbage", base::Value("garbage"));
  EXPECT_FALSE(ExtractRemoteFrameToken(malformed1, &token_with_predecessor));

  base::Value::Dict malformed2;
  malformed2.Set("token", base::Value("garbage"));
  EXPECT_FALSE(ExtractRemoteFrameToken(malformed2, &token_with_predecessor));

  base::Value::Dict malformed3;
  malformed3.Set("token", base::Value("beefbeefbeefbeefcafecafecafecafe"));
  malformed3.Set("predecessor", base::Value("garbage"));
  EXPECT_FALSE(ExtractRemoteFrameToken(malformed3, &token_with_predecessor));
}