Optimize Build Framework (OBF)
Sections:
- Intro
- Why use a framework?
- Before you get started – enable the integration
- Structure of a build
- Structure of post-render
- Enabling Debug
- Configuration params
- Framework modules and methods
- URL parameters
- Cookies
- Scenarios in detail: Conditional Activation
- Scenarios in detail: Polling
- Scenarios in detail: Page hide
- Scenarios in detail: Rendering levels
- Scenarios in detail: Working with CSS
- Scenarios in detail: CSS Media Queries
- Scenarios in detail: Redirection tests (split url testing)
- Scenarios in detail: Working with iframes
- Scenarios in detail: Conversions & custom data collection
- Scenarios in detail: Internal events system
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 |
---|---|---|
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, ornull
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);
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: 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.