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

configurable-arbitrary

v0.0.3

Published

QuickCheck Modules

Downloads

13

Readme

ConfigurableArbitrary helps you write intermediate and final data structures for JSVerify, a powerful QuickCheck library for Node.JS.

Background

JSVerify has a built-in type Arbitrary, which can generate stimulus for testing. JSVerify ships with arbitraries for primitive data types (string, number, etc.) and you can create your own generators.

However, JSVerify arbitraries can't be configured. Instead, JSVerify has seperate arbitraries for different use cases, including nestring to ensure the string has contents, and asciistring to ensure that characters can only come from the ASCII character set.

Shipping different arbitraries for primitive types works, but becomes impossible when dealing with larger data types. That's where ConfigurableArbitrary steps in.

Basic Usage Guide

ConfigurableArbitrary is base class that goes from run-time configuration to an Arbitrary, intended for creating object instances and other complex data types.

This process is broken into three stages to allow flexibility through stage-specific configuration or stages to be fully overridden.

Users should extend the ConfigurableArbitrary class to provide properties or replace classes. ConfigurableArbitrary comes with methods for each stage, starting with ConfigurableArbitrary.build. The class also contains internal methods to help some of the stages, and some basic utilities that simplify option definitions.

For this guide, we'll be creating a URLArbitrary, which can generate URL-like strings for testing purposes.

const ConfigurableArbitrary = require("configurable-arbitrary");

class URLArbitrary extends ConfigurableArbitrary {
  /* Properties and methods will be defined later in the documentation. */
}

module.exports = URLArbitrary;

Then in testing, we'll be able to use the arbitrary. First, we'll bake configuration options into the Arbitrary, converting the ConfigurableArbitrary into a a JSVerify Arbitrary.

const jsc = require("jsverify");
const URLArbitrary = require("./URLArbitrary.js");

let url = URLArbitrary.build({
  domain: jsc.constant("google.com"),
});

The configuration options are described in more depth later, but the domain will be merged with the protocol (e.g. https, etc.) and path to create the full URL string. We aren't specifying the protocol or path, so they will still be default values.

url is now a valid JSVerify Arbitrary. We can pass the Arbitrary to utilities like jsc.forall to run tests:

jsc.assert(jsc.forall(url, ({url}) => {
  return url.indexOf("://") > -1;
}));

Stage 1: Configuration

Out of the box, ConfigurableArbitrary comes without any configuration options. However, the class is setup to parse any options you'd like to define.

By default, these options won't be used in the final output. They are just to provide options to the actual generator.

The first stage involves merging option defaults from the extended class (and any other class between the final class and ConfigurableArbitrary), and merging these with the user-defined options.

Each sub-class of ConfigurableArbitrary can define an opts property that will be merged into the run-time configuration:

class URLArbitrary extends ConfigurableArbitrary {
  static get opts() {
    return {
      
      protocols: [ "http://", "https://" ],
      
      protocol: null,
      
      domain: null,
      
      path: null,
      
      title: null,
      
    };
  }
}

Stage 2: Arbitrary Specification

Because ConfigurableArbitrary was created with objects and complex data types in mind, it assumes that most users will want multiple Arbitrary values to configure.

Even for our simple URLArbitrary above, which will just output a single string and the page title, the configuration is split into several chunks so that users can override specific sections of the URL while leaving the other sections unchanged.

Each sub-class of ConfigurableArbitrary can define a spec method, which will be merged into an object that is passed to jsverify.record, which will merge them into an object so they can be passed as a single variable.

The spec object will be pre-populated with any options that already are Arbitrary objects.

const jsc = require("jsverify");

class URLArbitrary extends ConfigurableArbitrary {
  static spec(opts) {
    return {
      
      protocol: protocol => this.defaultArbitrary(protocol, () => this.protocol(opts.protocols)),
      
      domain: domain => this.defaultArbitrary(domain, jsc.nestring),
      
      path: path => this.defaultArbitrary(path, jsc.string),
      
      title: title => this.defaultArbitrary(title, jsc.string),
      
    };
  }
  
  static protocol(protocols) {
    return jsc.oneof(protocols.map(protocol => jsc.constant(protocol)));
  }
}

This example uses ConfigurableArbitrary.defaultArbitrary, which uses previously-defined arbitraries (such as arbitraries coming from run-time options) if they exist, otherwise it uses the defined arbitrary for this stage.

Stage 3: Transformations

ConfigurableArbitrary lets you define a transform method that is passed the Arbitrary created by jsc.record() in the previous step. By default, ConfigurableArbitrary uses a no-op/identity function that does not apply any transformation.

Unlike opts and spec which traverse the inheritance tree and merge all intermediate classes, transform is only called on the final step, and you must manually call super to get the result from intermediate classes.

class URLArbitrary extends ConfigurableArbitrary {
  static transform(arb) {
    return this.smapobj(arb, (opts) => {
      return {
        url: `${opts.protocol}${opts.domain}${opts.path}`,
        title: opts.title,
      };
    });
  }
}

This example uses ConfigurableArbitrary.smapobj, which automatically preforms a two-way mapping which is needed for jsverify.

Usage of URLArbitrary

Now that we've defined the URLArbitrary, we can use it in testing.

const jsc = require("jsverify");
jsc.ava = require("ava-verify");

const arb = URLArbitrary.build({
  path: jsc.constant("/"),
  title: jsc.constant("Google"),
});

jsc.ava({
  suite: "Creates URLs",
}, [ arb ], (t, url) => {
  t.not(url.url.indexOf("://"), -1);
});

This test is running via our ava-verify plugin for the AVA test runner, but the arbitrary will work in any JSVerify test platform.