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

45

v1.7.0

Published

A functionally-oriented test runner

Downloads

659

Readme

45

A functionally-oriented test runner

45 is the fast and functional test runner that is easy to use and gets out of your way.

Let me have it!

npm install --save-dev 45

Features

  • Does not rely on globals
  • Test failure if no assertions are returned
  • Lazy, monadic, and curried test assertions via 4.5
  • Promise, Observable, and Async/Await support
  • Runs all tests in parallel
  • ES2015 and TypeScript support out-of-box

Basic Usage

Create a test file

// test/foo.js

// ES2015
import { describe, given, it, equals } from '45';

export const test = describe('Array', [
  given('a few numbers', [
    it('has length greater than 0', () => {
      return equals([1, 2, 3].length > 0, true);
    })
  ])
])

export const otherTest = it('equals 4', () => {
  return equals(4, 4);
})

// commonjs
const { describe, given, it, equals } = require('45');

exports.test = describe('Array', [
  given('a few numbers', [
    it('has length greater than 0', () => {
      return equals([1, 2, 3].length > 0, true);
    })
  ])
])

exports.otherTest = it('equals 4', () => {
  return equals(4, 4);
})

In your terminal run

./node_modules/.bin/45 test/foo.js
# Supports globs
./node_modules/.bin/45 test/*.js

# or without parameters
# by default it will search for all .test and .spec files in src/ folder
# and for all files in test/ and tests/ folders
./node_modules/.bin/45

And you should see:

basic-test

Tests

When running 45 from the command line, it will look for all test files it can, collecting all exports, of no particular export name, that adhere to the Test interface described in the types section. This means that 45 can be extended to handle new test types not offered here via 3rd party libraries.

All 45 Tests must return objects adhering to the Assertion interface described in the Types section. Many assertions are re-exported by this library from 4.5. Though a number are provided by default -- 3rd party libraries implementing the Assertion interface can be used 100% freely.

describe(thing: string, tests: Array<Test>): Test

Allows collecting many Tests together as a larger whole. All tests are run in parallel.

import { describe } from '45';

export const test = describe('My Thing', [ ... ])

given(parameters: string, tests: Array<Test>): Test

Just like describe in that it allows collecting many Tests together as a larger whole, but allows for more descriptive test suites. All tests are run in parallel.

import { describe, given } from '45';

export const test = describe('My thing', [
  given(`a b and c`, [
    it('does ...', () => { ... })
  ])
])

it(does: string, testFn: TestFn): Test

Primitive test type which allows providing a callback to actually perform assertions. By default, will timeout at 2000 milliseconds.

import { it, pass } from '45';

export const test = it('does things', () => {
  return pass(1);
})

timeout(ms: number, test: Test): Test

Allows adjusting the amount of time a test can take to complete.

import { it, timeout, equals } from '45';

export const failing = it('fails', () => {
  return new Promise((resolve) => {
    setTimeout(resolve, 3000, 1)
  })
    .then(equals(1));
})

export const passing = timeout(3500, it('passes', () => {
  return new Promise((resolve) => {
    setTimeout(resolve, 3000, 1)
  })
    .then(equals(1));
}));

beforeEach(hook: () => any, tests: Array<Test>): Test

Allow running a hook before a series of tests. This will run the containing array of tests one after another.

import { beforeEach, describe, given, it } from './';

import { equals } from '4.5';

let x = 0;

export const test = describe('beforeEach', [
  given(`a function and an array of tests`, [
    beforeEach(() => { x++; }, [
      it('runs beforeEach test', () => {
        return equals(1, x);
      }),

      it('runs beforeEach test every time', () => {
        return equals(2, x);
      }),
    ]),
  ]),
]);

Assertions (re-exported from 4.5)

  • All functions of arity 2 or more are curried.
  • All types are defined below in the types section.

equals<A>(expected: A, actual: A): Assertion<A>

Asserts two values expected and actual to have value equality.

is<A>(expected: A, actual: A): Assertion<A>

Asserts two values expected and actual to have referential equality.

pass<A>(value: A): Assertion<A>

Creates an assertion which always passes with a given value.

fail(message: any): Assertion<void>

Creates an assertion which will always fail with a given message.

throws(fn: () => any): Assertion<Error>

Creates an assertion that tests that a given function throws and error.

Assertion combinators (re-exported from 4.5)

map<A, B>(fn: (a: A) => B, assertion: Assertion<A>): Assertion<B>

Given a function it maps one assertion value to another.

import { it, map, equals } from '45';

export const test = it('maps a value from type A to type B', () => {
  const add1 = (x: number) => x + 1;

  return map(add1 /* called with 1 */, equals(1, 1)) // Assertion<2>;
});

ap<A, B>(fn: Assertion<(a: A) => B>, value: Assertion<A>): Assertion<B>

Given an assertion containing a function from a to b, and an assertion of a returns an assertion of type b.

import { it, ap, pass } from '45';

export const test = it('applys fn to a value', () => {
  const add1 = (x: number) => x + 1;

  const fn: Assertion<(x: number) => number> = pass(add1);
  const value: Assertion<number> = pass(1);

  return ap(fn, value); // returns an assertion containing the value 2
});

chain<A, B>(fn: (a: A) => Assertion<B>, assertion: Assertion<A>): Assertion<B>

Given a function from one value a to Assertion b and an assertion of type a, returns an assertion of type b Useful for making many assertions using the values from the previous assertion.

import { it, chain, equals, map }

export const test = it('chains many assertions', () => {
  const add1 = (x: number) => x + 1;

  const oneIsOne = equals(1, 1);
  const isTwo = equals(2); // don't forget, all assertions are curried!
  const isThree = equals(3);

  return chain(isThree, map(add1, chain(isTwo, map(add1, oneIsOne))));
})

bimap

Type signature is to long for the header :smile:

bimap<A, B>(
  failure: (message: string) => string,
  success: (a: A) => B,
  assertion: Assertion<A>): Assertion<B>;

Similar to map but also allows adjusting error messages.

Useful for creating your own error messages.

import { bimap, equals, fail, pass } from '45';

export const test = it('allows adjusting error message', () => {
  return bimap(() => 'Sadly 1 is not correct', fail /* should not pass*/, fail(1));
});

concat(one: Assertion<A>, two: Assertion<A>): Assertion<A>

Chain together 2 assertions.

import { concat, pass, it } from '45';

export const test = it('concatenates', () => {
  return concat(pass(1), pass(2)); // passes if and only if both assertions pass.
});

Types

// 45 interfaces
export interface Test {
  name: string;
  timeout: number;
  run(): Promise<Assertion<any>>;
  showStatus: boolean;
}

export type TestFn =
  (() => Assertion<any>) |
  (() => Promise<Assertion<any>>) |
  (() => Observable<Assertion<any>>);

export interface TestResult {
  failures: number;
  message: string;
}

export type Observable<A> =
  {
    subscribe(observer: Observer<A>): Subscription;
  }

export type Observer<A> =
  {
    next(value: A): any;
    error(err: any): any;
    complete(): any;
  }

export type Subscription =
  {
    unsubscribe(): any;
  }

// 4.5 re-exported interfaces

export interface Assertion<T> {
  verify(verification: Verification<T>): void;
}

export interface Verification<T> {
  success(actual: T): any;
  failure(message: string): any;
}