npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

labyrinth-guards

v1.0.0-beta.1

Published

[![Coverage Status](https://coveralls.io/repos/github/morten-olsen/abtesting/badge.svg?branch=test)](https://coveralls.io/github/morten-olsen/abtesting?branch=test) [![Build Status](https://travis-ci.org/morten-olsen/abtesting.svg?branch=master)](https://

Downloads

5

Readme

A/B Testing

Coverage Status Build Status

TO-DO before releasable

  • Implement automatic impression tracking
  • Finalize tooling
  • Find a better name

Getting started

For a minimal setup six elements are needed: ABRoot, withABTest and VariantA/VariantB along with an ABManger with an ABTestSuite and an ABUser

A.jsx

import React, { Component } from 'react';
import { withABTest } from from 'abstesting';

class A extends Component {
  handleClick() {
    this.props.trackConversion();
  }

  render() {
    return (
      <div onClick={this.handleClick.bind(this)}>Variant A</div>
    );
  }
}

export default withABTest(A);

B.jsx

import React, { Component } from 'react';
import { withABTest } from from 'abstesting';

class B extends Component {
  handleClick() {
    this.props.trackConversion();
  }

  render() {
    return (
      <div onClick={this.handleClick.bind(this)}>Variant B</div>
    );
  }
}

export default withABTest(B);

App.jsx

import React from 'react';
import { VariantA, VariantB } from 'abtesting';
import A from './A';
import B from './B';

export default () => (
  <div>
    <VariantA name="test1" autoTrackImpression>
      <A />
    </VariantA>
    <VariantB name="test2" autoTrackImpression>
      <B />
    </VariantB>
  </div>
);

test1.js

import { FunctionTest } from 'abtesting';

export default new FunctionTest(
  (userInfo) => userInfo.age > 15,
  (userInfo) => userInfo.id % 2,
);

index.jsx

import React from 'react';
import ReactDOM from 'react-dom';
import { ABRoot, ABUser, ABTestSuite, ABManager } from 'abtesting';
import test1 from './tests';
import App from './App.js';

const manager = new ABManager({
  user: new ABUser(),
  testSuite = new TestSuite(),
});
manager.setUserInfo({
  id: 1234,
  age: 23,
});
manager.setTests({
  test1,
});

ReactDOM.render(
  <ABRoot manager={manager}>
    <App />
  </ABRoot>,
  document.body,
)

Using different components

Another usefull feature is to be able to serve different components depending on which test bucket the user is in. This can be done using the createABTest higher order function

A.jsx

export default ({value}) => <div>VariantA {value}</div>

B.jsx

export default ({value}) => <div>VariantB {value}</div>

Component.jsx

import { createABSplit } from 'abtesting';
import A from './A';
import B from './B';

export default createABSplit({
  name: 'test1',
  VariantA: A,
  VariantB: B,
})

Now when rendering <Component value="hello" />, the user which are presented the original version will see "VariantA hello" and user presented with the alternative version will see "VariantB hello".

User information

In order to figure out if users is in a given group, a set of user trades need to be provided. The test split is stateless, so therefore it can not store any information about previous a/b buckets a user has fallen into, and therefor this needs to be something which can be figured out based on values about the current user.

These are set by calling setUserInfo on the manager object, which was passed to ABRoot. When these values change, all A/B splits, currently presented to the user will be re-evaluated, and if a user changes bucket, these changes are reflected.

import { ABManager, ABUser, ABRoot } from 'abtesting';

const manager = new ABManager({
  ...
  user: new ABUser();
});
manager.setInfo({
  age: 23
});

const root = (
  <ABRoot manager={manager}>
  </ABRoot>
)

Tests

Test Suites

Creating a custom test

import { ABTest } from 'abstesting';
class CustomTest extends ABTest {
  isIncludedInTest(userInfo) {
    return userInfo.isLoggedIn;
  }

  isInTestGroup(userInfo) {
    return userInfo.id % 2;
  }
}

manager.setTest({
  someTest: new CustomTest(),
});

Services

The component comes with a few different service types preloaded and provides the tools for creating custom service

Creating a service

import { ABService, ABManager } from 'abstesting';

class CustomService extends ABService {
  constructor(endpoint) {
    super();
    this.endpoint = endpoint;
  }

  async updateTests() {
    const response = await fetch(endpoint).then(res => res.json());
    return response;
  }

  async sendData(data) {
    const data = new FormData();
    data.append( "json", JSON.stringify(data) );
    return fetch(endpoint, {
      method: "POST",
      body: data
    });
  }

  async trackImpression(name, type) {
    await this.sendData({
      type: 'impression',
      name,
      type,
    });
  }

  async trackConversion(name, type, params) {
    await this.sendData({
      type: 'impression',
      name,
      type,
    });
  }
}

const manager = new ABManager {
  ...
  service: new CustomService(),
}

const root = (
  <ABRoot manager={manager}>
  </ABRoot>
)