<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>Test basic autofill</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <script type="text/javascript" src="../formautofill_common.js"></script>
  <script type="text/javascript" src="../../../../../..//toolkit/components/satchel/test/satchel_common.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Form autofill test: simple form credit card autofill

<script>
"use strict";

const MOCK_STORAGE = [{
  "cc-name": "John Doe",
  "cc-number": "4929001587121045",
  "cc-exp-month": 4,
  "cc-exp-year": 2017,
}, {
  "cc-name": "Timothy Berners-Lee",
  "cc-number": "5103059495477870",
  "cc-exp-month": 12,
  "cc-exp-year": 2022,
}];

const reducedMockRecord = {
  "cc-name": "John Doe",
  "cc-number": "4929001587121045",
};

async function setupCreditCardStorage() {
  await addCreditCard(MOCK_STORAGE[0]);
  await addCreditCard(MOCK_STORAGE[1]);
}

async function setupFormHistory() {
  await updateFormHistory([
    {op: "add", fieldname: "cc-name", value: "John Smith"},
    {op: "add", fieldname: "cc-exp-year", value: 2023},
  ]);
}

initPopupListener();

// Form with history only.
add_task(async function history_only_menu_checking() {
  // TODO: eliminate the timeout when we're able to indicate the right
  // timing to start.
  //
  // After test process was re-spawning to https scheme. Wait 2 secs
  // to ensure the environment is ready to do storage setup.
  await sleep(2000);
  await setupFormHistory();

  await setInput("#cc-exp-year", "");
  synthesizeKey("KEY_ArrowDown");
  await expectPopup();
  checkMenuEntries(["2023"], false);
});

// Display credit card result even if the number of fillable fields is less than the threshold.
add_task(async function all_saved_fields_less_than_threshold() {
  await addCreditCard(reducedMockRecord);

  await setInput("#cc-name", "");
  await expectPopup();
  synthesizeKey("KEY_ArrowDown");
  checkMenuEntries([reducedMockRecord].map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-name"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `Visa ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));

  await cleanUpCreditCards();
});

// Form with both history and credit card storage.
add_task(async function check_menu_when_both_existed() {
  await setupCreditCardStorage();

  await setInput("#cc-number", "");
  await expectPopup();
  synthesizeKey("KEY_ArrowDown");
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primaryAffix: cc.ccNumberFmt.affix,
    primary: cc.ccNumberFmt.label,
    secondary: cc["cc-name"],
    ariaLabel: `${getCCTypeName(cc)} ${cc.ccNumberFmt.affix} ${cc.ccNumberFmt.label} ${cc["cc-name"]}`,
  })));

  await setInput("#cc-name", "");
  await expectPopup();
  synthesizeKey("KEY_ArrowDown");
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-name"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));

  await setInput("#cc-exp-year", "");
  await expectPopup();
  synthesizeKey("KEY_ArrowDown");
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-exp-year"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));

  await setInput("#cc-exp-month", "");
  await expectPopup();
  synthesizeKey("KEY_ArrowDown");
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-exp-month"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-month"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));

  await cleanUpCreditCards();
});

// Display history search result if no matched data in credit card.
add_task(async function check_fallback_for_mismatched_field() {
  await addCreditCard(reducedMockRecord);

  await setInput("#cc-exp-year", "");
  synthesizeKey("KEY_ArrowDown");
  await expectPopup();
  checkMenuEntries(["2023"], false);

  await cleanUpCreditCards();
});

// Display history search result if credit card autofill is disabled.
add_task(async function check_search_result_for_pref_off() {
  await setupCreditCardStorage();

  await SpecialPowers.pushPrefEnv({
    set: [["extensions.formautofill.creditCards.enabled", false]],
  });

  await setInput("#cc-name", "");
  synthesizeKey("KEY_ArrowDown");
  await expectPopup();
  checkMenuEntries(["John Smith"], false);

  await SpecialPowers.popPrefEnv();
});

let canTest;

// Autofill the credit card from dropdown menu.
add_task(async function check_fields_after_form_autofill() {
  canTest = await canTestOSKeyStoreLogin();
  if (!canTest) {
    todo(canTest, "Cannot test OS key store login on official builds.");
    return;
  }

  await setInput("#cc-exp-year", 202);

  synthesizeKey("KEY_ArrowDown");
  // The popup doesn't auto-show on focus because the field isn't empty
  await expectPopup();
  checkMenuEntries(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-exp-year"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));

  synthesizeKey("KEY_ArrowDown");
  let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
  await new Promise(resolve => SimpleTest.executeSoon(resolve));
  await triggerAutofillAndCheckProfile(MOCK_STORAGE[1]);
  await osKeyStoreLoginShown;
});

// Fallback to history search after autofill values (for non-empty fields).
add_task(async function check_fallback_after_form_autofill() {
  if (!canTest) {
    return;
  }

  await setInput("#cc-name", "J", true);
  synthesizeKey("KEY_ArrowDown");
  await expectPopup();
  checkMenuEntries(["John Smith"], false);
});

// Present credit card popup immediately when user blanks a field
add_task(async function check_cc_popup_on_field_blank() {
  if (!canTest) {
    return;
  }

  await setInput("#cc-name", "", true);
  await expectPopup();
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-name"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));
});

// Resume form autofill once all the autofilled fileds are changed.
add_task(async function check_form_autofill_resume() {
  if (!canTest) {
    return;
  }

  document.querySelector("#cc-name").blur();
  document.querySelector("#form1").reset();

  await setInput("#cc-name", "");
  synthesizeKey("KEY_ArrowDown");
  await expectPopup();
  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
    primary: cc["cc-name"],
    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
    ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
  })));
});

</script>

<p id="display"></p>

<div id="content">

  <form id="form1">
    <p>This is a basic form.</p>
    <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
    <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
    <p><label>Expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></label></p>
    <p><label>Expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></label></p>
    <p><label>Card Type: <select id="cc-type" autocomplete="cc-type">
      <option value="discover">Discover</option>
      <option value="jcb">JCB</option>
      <option value="visa">Visa</option>
      <option value="mastercard">MasterCard</option>
    </select></label></p>
    <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
  </form>
</div>

<pre id="test"></pre>
</body>
</html>
