Deploying Split Testing plugin

Administrators perform this task.

The Split Testing plugin must be enabled for a site for the A/B Promo module to be available for use. Currently, no themes have been styled to include the A/B Promo so custom Javascript must be added to the site.

  1. From the Navigation menu, select Admin > Sites & Settings.
  2. In the Sites widget, click Global. The Edit Global widget appears. (To enable Split Testing for a specific site only, select that site.)
  3. Under the Plugins tab, check Split Testing. (For specific sites, select Enabled for Split Testing.)
  4. Under Front-End, expand Advanced.
  5. Under Custom Scripts and Styles, click add_circle_outline.
  6. Enter a name for Internal Name for the custom element.
  7. Click Add under Elements and select Script Element.
  8. Enter a name for Internal Name for the script element.
  9. Enter the Javascript required by the frontend to support the A/B Promo module. See the reference code below.
  10. Click Save.

Reference implementation code for frontend developers:

window.addEventListener('load', event => {
   let nodes = []
   let iterator = document.createNodeIterator(
    document.body,
    NodeFilter.SHOW_ALL,
    () => NodeFilter.FILTER_ACCEPT,
    false
 )
 let current
 while ((current = iterator.nextNode())) {
    if (current.nodeType == Node.COMMENT_NODE) {
     let comment = current.nodeValue
     if (comment.startsWith('SplitStart')) {
       let [_, id, numOfVariants, testAudiencePercentage] = comment.split(' ')
       // This assumes that there will only be one node between the opening and closing comments
       let openingCommentNode = current
       let contentNode = openingCommentNode.nextSibling
       let closingCommentNode = contentNode.nextSibling
       // Clean-up
       openingCommentNode.remove()
       closingCommentNode.remove()
       nodes.push([contentNode, id, numOfVariants, testAudiencePercentage])
     }
   }
  }
  nodes.forEach(element => {
   let [content, id, numOfVariants, testAudiencePercentage] = element
   // Select the variants randomly but remain consistent across page loads
   let selection = localStorage.getItem('variant.' + id)
   if (selection == null) {
      // Determine if user is to be part of the test audience
    if (Math.floor(Math.random() * 100) < testAudiencePercentage) {
      // Generate random integer between 1 and the number of variants, inclusive of 1
       selection = Math.floor(Math.random() * (numOfVariants - 1)) + 1
    } else {
      // Select Original
      selection = 0
    }
    localStorage.setItem('variant.' + id, selection)
  }
  fetch('?renderedSplitId=' + id + '&variantSelection=' + selection)
    .then(response => response.json())
    // Converts string from json into html fragrment
    .then(json =>
      document.createRange().createContextualFragment(json.fragment)
    )
    // Grabs the node from Fragment
    .then(fragment => fragment.firstChild)
    // Adds node to DOM
    .then(node => {
      content.replaceWith(node)
      return node
      // Adds listener to track clicks
    })
    .then(node => {
      node.querySelectorAll('a').forEach(a => {
        a.addEventListener('click', () => {
          fetch('/_split', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
            },
            body:
              'trackMode=click&splitId=' + id + '&splitVariant=' + selection
          })
        })
      })
      // Reports impression
    })
    .then(() =>
      fetch('/_split', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body:
          'trackMode=impression&splitId=' + id + '&splitVariant=' + selection
      })
    )
  })
})