Skip to content
  • There are no suggestions because the search field is empty.

Product Name Testing

Product name tests consists of two parts:

  1. Frontend changes using vanilla JS — update product names across HP, Recommendations, PLP, PDP, etc.
  2. Cart/checkout changes — possible only on Shopify Plus via a Shopify function (cart-transformer).

If you are on Shopify Plus, our team can install the cart-transformer function, let us know, and we’ll deploy it.


Changing the name with javascript:

As an example, to change a fictional product name from: “The Original Product” to “The Best Product” we will start by defining variables so it will be easy to change the name:

const FROM = "The original product";
const TO = "The best product";

Then we will define a function that will make the actual change of the name, this is a naive one and you can switch it to work on your custom logic:

function replaceTextNodes(src, replacement) {
  console.debug("vsly-name-change", "replaceTextNodes");
  try {
    Array.from(
      document.querySelectorAll(`*:not([vslyNameChanged="true"]:only-child`)
    )
      .filter((e) =>
        Array.from(e.childNodes).some((a) => {
          return a.nodeType === 3 && a.nodeValue?.includes(src);
        })
      )
      .forEach((e) => {
        const textNode = Array.from(e.childNodes).find(
          (a) => a.nodeType === 3 && a.nodeValue?.includes(src)
        );
        textNode.nodeValue = textNode.nodeValue?.replace(src, replacement);
        e.setAttribute("vslyNameChanged", "true");
      });
  } catch (ex) {
    console.error(`vsly-name-change`, ex);
  }
}


And we will listen for changes in the DOM with debounce:

function listenForChanges() {
  const observer = new MutationObserver(onDomChanged);
  observer.observe(document.body, {
    subtree: true,
    childList: true,
  });
}

const onDomChanged = loomi_api.debounce(() => {
  replaceTextNodes("Beach Oodie", "Poncho Towel");
}, 200);


And finally call both of the function on boot:

function boot() {
  console.debug("vsly-name-change", "boot");
  replaceTextNodes(FROM, TO);
  setTimeout(() => listenForChanges(), 25);
}
boot();


Changing the name on cart/checkout with Shopify function:

The 2nd part is to change the name on cart/checkout changes, to do that we need the cart-transformer function. We can control the cart-transformer using a cart-attribute property that contains the configuration:

First we will define three helper methods:

function updateProperty(attr) {
  fetch("/cart/update.js?vsly=t", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      attributes: { vsly_rename_map: attr },
    }),
  });
}

function decodeHex(hex) {
  return hex
    .split(/(\w\w)/g)
    .filter((p) => !!p)
    .map((c) => String.fromCharCode(parseInt(c, 16)))
    .join("");
}

function encodeHex(plain) {
  return plain
    .split("")
    .map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
    .join("");
}


And the function that will maintain this cart attribute:

function maintainCartAttr() {
  const attr = loomi_ctx.cart.attributes["vsly_rename_map"];
  console.debug("vsly-name-change", "maintainCartAttr", attr);
  let updatedAttr = "";
  if (!attr) {
    updatedAttr = encodeHex(JSON.stringify([{ from: FROM, to: TO }]));
  } else {
    const rename = JSON.parse(decodeHex(attr));
    const found = rename?.find((row) => row.from === FROM);
    if (!found) {
      rename.push({ from: FROM, to: TO });
      updatedAttr = encodeHex(JSON.stringify(rename));
    }
  }

  console.debug(
    "vsly-name-change",
    "maintainCartAttr updated",
    attr,
    updatedAttr
  );
  if (updatedAttr) {
    console.debug(
      "vsly-name-change",
      "maintainCartAttr running update cart attr",
      attr,
      updatedAttr
    );
    updateProperty(updatedAttr);
  }
}


And update the boot function from last step to call to this method as well:

function boot() {
  console.debug("vsly-name-change", "boot");
  replaceTextNodes(FROM, TO);
  setTimeout(() => listenForChanges(), 25);
  loomi_api
    .when(() => !!loomi_ctx.cart)
    .then(() => {
      maintainCartAttr();
    });
}
boot();

 


To complete the test we recommend to create a clean variant that will run on 100% whenever the test is ended with the following code:

function updateProperty(attr) {
  fetch("/cart/update.js?vsly=t", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      attributes: { vsly_rename_map: attr },
    }),
  });
}

function cleanup() {
  const attr = loomi_ctx.cart.attributes["vsly_rename_map"];
  if (attr) {
    updateProperty(``)
  }
}

loomi_api
    .when(() => !!loomi_ctx.cart)
    .then(cleanup);


This will ensure that carts that already have the rename property will be cleanup and won’t affect next tests