Optimize Build Framework (OBF)

Sections:

Intro

The Optimize Build Framework (OBF), is a framework that faciliates easier coding in our Advanced Editor. Pre-defined methods, scripts and modules are tailored for use in our platform.

The OBF acts abstracts away the more challenging aspects of building a test, proving simple-to-understand methods documented herein.

Why use a framework?

Pros:
  • Simpler code – Doing complex things becomes a lot more straightforward with the utility methods provided as part of the OBF.
  • Code consistency – if you have a team of developers, all of their work starts to look more similar. Debugging someone else’s work becomes a lot more straightforward too.
  • Rich debugging and error handling – Console logging and alerting are hidden from end users behind a developer cookie. Error handling is more graceful, being picked up inside sandboxed function calls and logged to developers so they can investigate.
  • Helper methods – Beyond what we provide in the tag, the OBF has a range of helper/utility methods to make certain things much more straightforward. The module ecosystem also allows you to write your own.
  • Building better tests – With polling, page hiding, granular control over View tracking/entry, etc. – you can build much more robust tests.
Cons:
  • It’s a big framework, so there’s a learning curve to go through.
  • Weight – sometimes it can feel like bringing a rocket launcher to a knife-fight.

Before you get started – enable the integration

The OBF is a framework that needs to be enabled. Given not all customers need it, we don’t include it in all tags by default – that would add weight to the tags of those who don’t need it

To enable the OBF framework in your tag:

  • Head over to Manage > Interations, found in the navbar.
  • Add a new integration.
  • Find the Optimize build framework – it’ll be in Developer Tools if you can’t spot it.
  • Install it
  • Switch the toggle to ON
  • Select the tags you want it for – staging ones, live, or all.
  • Click Save
  • Give it about 10 minutes for the CDN tag to update. You may need to open up the tag in a separate tag and hard-refresh (or do a clear cache and hard refresh in your browser) as the file is cached.

Structure of a build

Normally, you would put the code for your variation into Level Content. This is not the case with OBF. It uses a single JS file to handle all of the code you need to manage (in post-render, see below), and the remaining sections just contain constructors and references.

  • Pre-Render – Initialises the namespace that we use for your test. Will lift a few settings out of Velocity to help simplify things too.
  • Level Content – Simply sets the reference in the OBF using setLevel method, so we know which level the user was served.
  • Post-Render – Where all of the configurable parts of your build are kept. See below.

Structure of post-render


Enabling Debug

Console.log statements in the Render section of the OBF are masked by a cookie check. This means that end-users won’t be able to see your logs, but as a developer you can drop the cookie and see what’s going on.

At the top of Render, you’ll see a mapping of var console = Test.debug, which is how this happens. By mapping console to Test.debug, you can write your transformations in the console and see the logs, safe in the knowledge that once they’re in the OBF, they won’t be visible anymore.

To enable debugging there are two routes you can take:

  • Use the query string parameter _wt.bdebug=true
  • Use the cookie _wt.bdebug=true

If you use the query string value, it will create the cookie for you, so this experience will stick with you for your session.

To help create the cookie, you can drag this link into your bookmarks bar: OBF Debug Cookie

Once copied into your bookmarks bar, simply click it and refresh the page, and console.log statements will start to appear


Configuration params

The OBF is highly configurable. The Config object in your post-render script is where most of these settings are kept. You’ll find the options and possible values listed below.

Build-specific

Param Value Description
baseURL Type: String Default: "" When referencing assets in your build, it is easier and lighter to specify the baseURL (folder) udner which they can be found, and then simply build your HTML (using JS) with code similar to: var html = '‹img alt="" src="'+ Config.baseURL +'myimage.png"'›';
testAlias Type: String Default: As retrieved from Velocity Storing the testAlias for the project allows us to hook into internal events systems and perform some checks seamlessly.
testName Type: String Default: Your test name. The OBF Debug output includes this at the very start, so you know exactly what you're looking at when debugging.
developer Type: String Default: Your name. When looking into debugging logs, you'll know who built the test. Useful for anyone developing to know who to go to for problem resolution when they see an error.

Conversion Tracking

Param Value Description
convDelay Type: Integer Default: 800 If delaying conversions with Test.conversion, this is the time we will wait before redirecting.
cpoints Type: Boolean Default: false If true, highlights DOM nodes which have the classname specified in cssConvNode.
cssConvNode Type: String Default: .wto-cp { border: 2px solid orange !important; } CSS rule to highlight conversion nodes – the class (.wto-cp by default) itself has to be assigned manually by developer to nodes in Tracking() for this to work.
useBeacon Type: Boolean Default: false Uses beacon mode for conversions. Similar to 1px GIF tracking.
useCTrack Type: Boolean Default: false Use CTrack for conversions (REST API) instead of in-tag methods (JS API).
skipPageviewCheck Type: Boolean Default: false We need a View before we track a Metric. The OBF will automatically and safely reject conversions fired before then, but you can request to skip the check.

Page Hiding

Param Value Description
cssHide Type: String Default: body { opacity: 0.0000001 !important; } The CSS rules we want to employ when page-hiding in the OBF.
cssHideID Type: String Default: "" ID of the style tag that houses the page hide.
hidePage Type: Boolean Default: true Whether or not to employ page hiding. If your test has transformations behind user actions or on content that mutates into view, you may want to leave this off.
hideTimeout Type: Integer Default: 7000 If the worst happens and the build freezes processing, this number defines the time in miliseconds that we should wait before forcibly redisplaying the page.
hideTimedOut Type: Boolean Default: false A status. Will be true if the page hide was forcibly removed.
showPage Type: Boolean Default: true Whether you should rediaplay the page after Rendering or not. If you're doing a redirect test, the answer may well be to leave page-hide on.
showPageDelay Type: Integer Default: 0 If you want to wait for a period of time before revealing the page, you can do that here (in miliseconds). No delay by default.

CSS module

Param Value Description
cssPrefixClass Type: String Default: .wto By default, CSS rules added via. css.add will be prefixed with this class. This is to increase the specificity of our rules above those listed, and increases the likelihood of them being selected by the browser.
classNamesOnDoc Type: Boolean Default: false If set to true, level-specific class names will be added to the ‹html› tag instead of default ‹body›. Sometimes this is desired as in some cases additional classes on body might break default site functionality.

Debugging

Param Value Description
debug Type: Boolean Default: false Forcibly show debug logs in the console for this instance of the OBF. Skips the check for the _wt.bdebug cookie.
debugInTitle Type: Boolean Default: true When true, we output level and test names into the document title. This only happens if in debug mode.
outputConfig Type: Boolean Default: true Outputs the Config object for your build into the console when your build executes.
debugPrefix Type: String Default: null Provides the ability to output something else to prefix debug logs instead of the testAlias.
showInternalEvents Type: Boolean Default: false Enables the event module to log internal events in the console. Possibly useful for debugging.
pvDebug Type: Boolean Default: false Enables extra debugging to understand what's happening with the Pageview event.

Conversion Tracking

Param Value Description

Framework modules and methods

TODO

URL parameters

TODO

Cookies

The OBF comes with built-in ways of handling common cookie operations - allowing you to read, compare, create, delete. These can be found inside of Test.cookie

.get

Retrieves the value of a cookie, or null if no value is found.
Test.cookie.get(str_cookieName);

.del

Deletes the cookie with a given name.
Test.cookie.del(str_cookieName);

.set

Creates a cookie with these params:
  • Name: required, string. Name of the cookie
  • Value: required, string. Value of the cookie
  • Duration: optional, number. How long in days the cookie should expire in. If not provided, it will be a session cookie.
  • Path: optional, string. The pathname param in the cookie
  • Domain: optional, string. The domain param in the cookie
// General format 
Test.cookie.set(str_name, str_value, num_duration, str_path, str_domain);

// Session cookie 
Test.cookie.set("myCookie", "the value");

// Persistent Cookie - 30 days
Test.cookie.set("myCookie", "the value", 30);

.has

TODO

Scenarios in detail: Conditional Activation / Manual Entry / Pageview Handling

We've made this nice and simple. The Run function specifies test execution, and when coupled with some handy helper methods allows you to choose when to activate your test.

If your condition is available to read before the tag triggers.

The below method ensures that no visitors are included in the test if they match our condition. They will continue to be tracked if they have been counted previously, though.
Run: function()
{
    if(document.cookie.match(/userType=business/i)){
        Test.abort("We don't want to include business users.");
        return;
    }
    
    Test.poll({
        msg: 'jQuery + body polling',
        // Polling function
        when: function()
        {
            return window.jQuery && jQuery('footer').length;
        },
        // Polling callback
        then: function()
        {
            Test.start('Rendering', Test.Render);
            Test.start('Tracking', Test.Tracking);

            // SHOW PAGE
            Test.showHidePage(Config.showPage);
        }
    });
},

If you need to wait for an element before knowing if it's ok to run your test

In this example, we show you how to run a test only if your page has errors on it. Again, visitor are only counted if meet our condition.
Run: function()
{
    Test.pageview.suspend("waiting for an element before we know it's ok to proceed");
    
    Test.poll({
        msg: 'jQuery + body polling',
        // Polling function
        when: function()
        {
            return window.jQuery && jQuery('footer').length;
        },
        // Polling callback
        then: function()
        {
            if(!jQuery('label.error').length){ // If no errors, abort the test 
                Test.abort("No errors found");
                return;
            }
            
            Test.pageview.track(); // Fine to count visitors now.
            
            Test.start('Rendering', Test.Render);
            Test.start('Tracking', Test.Tracking);

            // SHOW PAGE
            Test.showHidePage(Config.showPage);
        }
    });
},

If you need to wait for an event before activating your test

In this example, we wait for someone to click on an element before we activate the test.
Run: function()
{
    Test.pageview.suspend("waiting for a click before we know it's ok to proceed");
    
    // Start by polling for jQuery 
    Test.poll({
        msg: 'jQuery polling',
        // Polling function
        when: function()
        {
            return window.jQuery;
        },
        // Polling callback
        then: function()
        {
            // Set your event hook 
            jQuery('body').on('click', '#myelm', function(){
                
                Test.pageview.track(); // Fine to count visitors now.
                
                Test.start('Rendering', Test.Render);
                Test.start('Tracking', Test.Tracking);
    
                // SHOW PAGE
                Test.showHidePage(Config.showPage);

            });
        }
    });
},

Scenarios in detail: Polling

Polling is the act of recursively checking for a condition to match, and taking action when it does. Typically for tests, this means waiting for your elements to arrive on the page before transforming them. The syntax for polling (with all options) is:
// Poll
Test.poll({
   when: function(){
      return (condition)
   },
   then: function(){
      // Do something
   },
   msg: 'Message to write to console while polling',
   timeout: 10000,
   forceExec: false,
   onTimeout: function(){
      // Do something 
   }
});
Params for object:
  • when: required, function. Should return the condition you're polling for so we know what success is.
  • then: required, function. What you want to happen if we succeed.
  • msg: optional, string. Message to write to the console while polling.
  • timeout: optional, integer. Default 7000. How long to poll for before breaking.
  • forceExec: optional, boolean. Default false. Whether or not we should execute "then" on failure.
  • onTimeout: optional, function. To be executed on failure to match the "when" condition.

Example: Poll for jQuery and the footer

// Poll
Test.poll({
   when: function(){
      return window.jQuery && jQuery('footer').length;
   },
   then: function(){
      // Do something
   },
   msg: 'Polling for jQuery and footer'
});

Scenarios in detail: Page hide

TODO

Scenarios in detail: Rendering levels

TODO

Scenarios in detail: Working with CSS

The OBF comes with references to a css method. This allows input either by a single String of CSS (with however many rules you like, or an Array of strings.

// Single input
css.add('#mybutton { background: red; } .secondarybutton { background: grey }');

// Multiple input
css.add([
   '#mybutton { background: red; }',
   '.secondarybutton { background: grey }'
]);

By using Javascript to write your CSS, you can easily make your rules conditional.


Scenarios in detail: CSS Media Queries

TODO

Scenarios in detail: Redirection tests (split url testing)

TODO

Scenarios in detail: Working with iframes

TODO

Scenarios in detail: Conversions & custom data collection

TODO

Scenarios in detail: Internal events system

TODO