Gottwik/Enduro

View on GitHub
libs/ab_testing/ab_tester.js

Summary

Maintainability
A
55 mins
Test Coverage
// * ———————————————————————————————————————————————————————— * //
// *     ab tester
// *    handles a/b testing
// * ———————————————————————————————————————————————————————— * //
const ab_tester = function () {}

// * vendor dependencies
const _ = require('lodash')

// * enduro dependencies
const page_queue_generator = require(enduro.enduro_path + '/libs/page_rendering/page_queue_generator')

// * ———————————————————————————————————————————————————————— * //
// *     get ab list
// *     generates list of ab testing races
// *
// *    list looks like this:
// *    {
// *        index: [
// *            {
// *                page: 'index'
// *            }, {
// *                page: 'index@ab'
// *            }, {
// *                page: 'index@bb'
// *            }
// *        ],
// *        test: [
// *            {
// *                page: 'test'
// *             }, {
// *                 page: 'test@bigbutton'
// *             }
// *        ]
// *    }
// *
// *    @return {object} - array of testing races
// * ———————————————————————————————————————————————————————— * //
ab_tester.prototype.get_ab_list = function () {
    return page_queue_generator.get_all_pages()
        .then((pages) => {

            // gets page list
            pages = pages.map((page) => {
                return page_queue_generator.get_page_url_from_full_path(page).split('@')
            })

            // groups by page
            pages = _.groupBy(pages, (page) => { return page[0] })

            // removes first item
            for (p in pages) {
                pages[p].shift()
                pages[p].map((page) => {
                    page.shift()
                })

                if (!pages[p].length) {
                    delete pages[p]
                    continue
                }

                pages[p].unshift([])

                // add urls and paths
                pages[p] = pages[p].map((page) => {

                    const file = page.length ? p + '@' + page : p

                    return {
                        page: file
                    }
                })
            }

            return pages
        })
}

ab_tester.prototype.get_ab_tested_filepath = function (url, req, res) {
    const self = this

    // removes slash from the front
    page_name = url[0] == '/' ? url.substring(1) : url

    return new Promise(function (resolve, reject) {
        self.generate_global_ab_list_if_nonexistent()
            .then(() => {

                // return if page does not have an ab_tests
                if (!(page_name in global.ab_test_scenarios)) {
                    return resolve(url)
                }

                const ab_scenario = global.ab_test_scenarios[page_name]

                let picked_variation

                // check if user has cookie for this url
                if (req.cookies['enduro_ab_' + url]) {
                    picked_variation = req.cookies['enduro_ab_' + url]
                } else {
                    picked_variation = ab_scenario[Math.floor(Math.random() * ab_scenario.length)]
                    res.cookie('enduro_ab_' + url, picked_variation, { maxAge: 900000, httpOnly: true })
                }

                if (picked_variation.page == 'index') {
                    resolve('/index')
                } else {
                    resolve('/' + picked_variation.page + '/index')
                }
            })
    })
}

ab_tester.prototype.generate_global_ab_list_if_nonexistent = function () {
    const self = this

    return new Promise(function (resolve, reject) {
        if (typeof global.ab_test_scenarios === 'undefined') {
            self.generate_global_ab_list()
                .then(() => {
                    resolve()
                })
        } else {

            resolve()
        }
    })
}

ab_tester.prototype.generate_global_ab_list = function () {
    const self = this

    return self.get_ab_list()
        .then((ab_list) => {
            global.ab_test_scenarios = ab_list
        })
}

module.exports = new ab_tester()