"use strict";

/* exported asyncElementRendered, promiseStateChange, promiseContentToChromeMessage, deepClone,
   PTU, registerConsoleFilter, fillField, importDialogDependencies */

const PTU = SpecialPowers.Cu.import(
  "resource://testing-common/PaymentTestUtils.jsm",
  {}
).PaymentTestUtils;

/**
 * A helper to await on while waiting for an asynchronous rendering of a Custom
 * Element.
 * @returns {Promise}
 */
function asyncElementRendered() {
  return Promise.resolve();
}

function promiseStateChange(store) {
  return new Promise(resolve => {
    store.subscribe({
      stateChangeCallback(state) {
        store.unsubscribe(this);
        resolve(state);
      },
    });
  });
}

/**
 * Wait for a message of `messageType` from content to chrome and resolve with the event details.
 * @param {string} messageType of the expected message
 * @returns {Promise} when the message is dispatched
 */
function promiseContentToChromeMessage(messageType) {
  return new Promise(resolve => {
    document.addEventListener("paymentContentToChrome", function onCToC(event) {
      if (event.detail.messageType != messageType) {
        return;
      }
      document.removeEventListener("paymentContentToChrome", onCToC);
      resolve(event.detail);
    });
  });
}

/**
 * Import the templates and stylesheets from the real shipping dialog to avoid
 * duplication in the tests.
 * @param {HTMLIFrameElement} templateFrame - Frame to copy the resources from
 * @param {HTMLElement} destinationEl - Where to append the copied resources
 */
function importDialogDependencies(templateFrame, destinationEl) {
  let templates = templateFrame.contentDocument.querySelectorAll("template");
  isnot(templates, null, "Check some templates found");
  for (let template of templates) {
    let imported = document.importNode(template, true);
    destinationEl.appendChild(imported);
  }

  let baseURL = new URL("../../res/", window.location.href);
  let stylesheetLinks = templateFrame.contentDocument.querySelectorAll(
    "link[rel~='stylesheet']"
  );
  for (let stylesheet of stylesheetLinks) {
    let imported = document.importNode(stylesheet, true);
    imported.href = new URL(imported.getAttribute("href"), baseURL);
    destinationEl.appendChild(imported);
  }
}

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * @param {HTMLElement} field
 * @param {string} value
 * @note This is async in case we need to make it async to handle focus in the future.
 * @note Keep in sync with the copy in head.js
 */
async function fillField(field, value) {
  field.focus();
  if (field.localName == "select") {
    if (field.value == value) {
      // Do nothing
      return;
    }
    field.value = value;
    field.dispatchEvent(new Event("input", { bubbles: true }));
    field.dispatchEvent(new Event("change", { bubbles: true }));
    return;
  }
  while (field.value) {
    sendKey("BACK_SPACE");
  }
  sendString(value);
}

/**
 * If filterFunction is a function which returns true given a console message
 * then the test won't fail from that message.
 */
let filterFunction = null;
function registerConsoleFilter(filterFn) {
  filterFunction = filterFn;
}

// Listen for errors to fail tests
SpecialPowers.registerConsoleListener(function onConsoleMessage(msg) {
  if (
    msg.isWarning ||
    !msg.errorMessage ||
    msg.errorMessage == "paymentRequest.xhtml:"
  ) {
    // Ignore warnings and non-errors.
    return;
  }
  if (
    msg.category == "CSP_CSPViolationWithURI" &&
    msg.errorMessage.includes("at inline")
  ) {
    // Ignore unknown CSP error.
    return;
  }
  if (
    msg.message &&
    msg.message.includes("Security Error: Content at http://mochi.test:8888")
  ) {
    // Check for same-origin policy violations and ignore specific errors
    if (
      msg.message.includes("icon-credit-card-generic.svg") ||
      msg.message.includes("accepted-cards.css") ||
      msg.message.includes("editDialog-shared.css") ||
      msg.message.includes("editAddress.css") ||
      msg.message.includes("editDialog.css") ||
      msg.message.includes("editCreditCard.css")
    ) {
      return;
    }
  }
  if (msg.message == "SENTINEL") {
    filterFunction = null;
  }
  if (filterFunction && filterFunction(msg)) {
    return;
  }
  ok(false, msg.message || msg.errorMessage);
});

SimpleTest.registerCleanupFunction(function cleanup() {
  SpecialPowers.postConsoleSentinel();
});
