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

brittle

v3.7.0

Published

A TAP test runner built for modern times

Downloads

7,565

Readme

brittle

tap à la mode

A TAP test runner built for modern times.

Usage

First install brittle from npm

npm i brittle

Then start writing tests

import test from 'brittle'

test('basic', function (t) {
  t.is(typeof Date.now(), 'number')
  t.not(typeof Date.now(), 'string')

  t.ok(Date.now() > 0)
  t.absent(null)

  t.comment('text')

  t.alike({ a: 1 }, { a: 1 })
  t.unlike({ a: 2 }, { a: 3 })

  t.pass()
  t.fail()
})

test('asynchronous', async function (t) {
  await new Promise(r => setTimeout(r, 250))
  t.pass()
})

test('plans', function (t) {
  t.plan(2)
  t.pass()
  setTimeout(() => t.pass(), 250)
})

test('classic subtest', function (t) {
  t.test('subtest', function (sub) {
    sub.plan(1)
    sub.pass()
  })
})

test('inverted subtest', function (t) {
  const sub = t.test('subtest')
  sub.plan(1)
  sub.pass()
})

test('executions', async function (t) {
  t.execution(() => 'should not throw')
  await t.execution(async () => 'should not reject')
})

test('exceptions', async function (t) {
  t.exception(() => { throw Error('expected to throw') })
  await t.exception(async () => { throw Error('expected to reject') })
})

const a = test('inverted test without plan needs end()')
a.pass()
a.end()

const b = test('inverted test with plan')
b.plan(1)
b.pass()

const c = test('inverted tests can be awaited')
c.plan(1)
setTimeout(() => c.pass(), 250)
await c

Every assertion can have a message, i.e. t.pass('msg'), t.ok(false, 'should be true'), etc.
There are also utilities like t.timeout(ms), t.teardown(fn), etc.
Check the API but also all the assertions here and utilities here.

API

import { test, solo, skip, hook, todo, configure } from 'brittle'

test([name], [options], callback)

Create a classic test with an optional name.

Available options for any test creation:

  • timeout (30000) - milliseconds to wait before ending a stalling test.
  • solo (false) - Skip all other tests except the solo() ones.
  • hook (false) - setup and teardown resources.
  • skip (false) - skip this test, alternatively use the skip() function.
  • todo (false) - mark this test as todo and skip it, alternatively use the todo() function.

The callback function (can be async) receives an object called assert.
assert (or t) provides the assertions and utilities interface.

import test from 'brittle'

test('basic', function (t) {
  t.pass()
})

Test files can be executed directly with node, as they're normal Node.js programs.

The test method is conveniently both the default export and named exported method:

import { test } from 'brittle'

Classic tests will run sequentially, buffering pending tests until any prior test catches up.

Any test function returns a promise so you can optionally await for its result:

const isOk = await test('basic', function (t) {
  t.pass()
})

test([name], [options]) => assert

Create an inverted test with an optional name.

All options for inverted tests are listed here.

An object called assert (or t) is returned, the same as the classic test.

This time it's also a promise, it can be awaited and it resolves at test completion.

import test from 'brittle'

const t = test('basic')

t.plan(1)

setTimeout(() => {
  t.pass()
}, 1000)

await t // Won't proceed past here until plan is fulfilled

For inverted tests without a plan, the end method must be called:

const t = test('basic')

setTimeout(() => {
  t.pass()
  t.end()
}, 1000)

await t

The end() method can be called inline, for inverted tests without a plan:

const t = test('basic')
t.pass()
t.end()

Control flow of inverted is entirely dependent on where its assert is awaited.
The following executes one test after another:

const a = test('first test')
const b = test('second test')
a.plan(1)
b.plan(1)
a.pass()
await a
b.pass()
await b

Awaiting the promise gives you its result:

const t = test('first test')
t.plan(1)
t.pass()
const isOk = await t

t.test([name], [options], callback)

t.test([name], [options]) => assert

A subtest can be created by calling test on an assert (or t) object.
This will provide a new sub-assert object.

All options for subtests are listed here.

Using this in inverted style can be very useful for flow control within a test:

test('basic', async function (t) {
  const a = t.test('sub test')
  const b = t.test('other sub test')

  a.plan(1)
  b.plan(1)

  setTimeout(() => a.ok(true), Math.random() * 1000)
  setTimeout(() => b.ok(true), Math.random() * 1000)
  
  // Won't proceed past here until both a and b plans are fulfilled
  await a
  await b

  t.pass()
})

Subtest test options can be set by passing an object to the test function:

test('parent', { timeout: 1000 }, function (t) {
  t.test('basic using parent config', async function (t) {
    await new Promise(r => setTimeout(r, 500))
    t.pass()
  })

  t.test('another basic using parent config', function (t) {
    t.pass()
  })
})

You can also await for its result as well:

test('basic', async function (t) {
  t.plan(1)
  t.pass()
  const isOk = await t
  console.log(isOk)
})

solo([name], [options], callback)

solo([name], [options]) => assert

Filter out other tests by using the solo method:

import { test, solo } from 'brittle'

test('this test is skipped', function (t) {
  t.pass()
})

solo('some test', function (t) {
  t.pass()
})

If a solo function is used, test functions will not execute.\

If solo is used in a future tick (for example, in a setTimeout callback),
after test has already been used those tests won't be filtered.

A few ways to enable solo functions:

  • Use configure({ solo: true }) before any tests.
  • You can call solo() without callback underneath the imports.
  • Using the --solo flag with the brittle test runner.

It can also be used as an inverted test:

const t = test.solo('inverted some test')
t.pass()
t.end()

skip([name], [options], callback)

Skip a test:

import { test, skip } from 'brittle'

skip('this test is skipped', function (t) {
  t.pass()
})

test('middle test', function (t) {
  t.pass()
})

test.skip('another skipped test', function (t) {
  t.pass()
})

Only the middle test will be executed.

hook([name], [options], callback)

Use before tests for setting up and after tests for tearing down. Runs the same way as test except always executes regardless of solo usage.

import { test, solo, hook } from 'brittle'

hook('setup hook', function (t) {
  // setup resources
})

solo('solo test', function (t) {
  t.pass()
})

test('middle test', function (t) {
  t.pass()
})

hook('teardown hook', function (t) {
  // teardown resources
})

The setup hook, solo test and teardown hook will be executed.

configure([options])

The configure function can be used to set options for all tests (including child tests).
It must be executed before any tests.

Options

  • timeout (30000) - milliseconds to wait before ending a stalling test
  • bail (false) - exit the process on first test failure
  • solo (false) - skip all other tests except the solo() ones
  • source (true) - shows error source information
import { configure } from 'brittle'

configure({ timeout: 15000 }) // All tests will have a 15 seconds timeout

Assertions

t.is(actual, expected, [message])

Compare actual to expected with ===

t.not(actual, expected, [message])

Compare actual to expected with !==

t.alike(actual, expected, [message])

Object comparison, comparing all primitives on the actual object to those on the expected object using ===.

t.unlike(actual, expected, [message])

Object comparison, comparing all primitives on the actual object to those on the expected object using !==.

t.ok(value, [message])

Checks that value is truthy: !!value === true

t.absent(value, [message])

Checks that value is falsy: !!value === false

t.pass([message])

Asserts success. Useful for explicitly confirming that a function was called, or that behavior is as expected.

t.fail([message])

Asserts failure. Useful for explicitly checking that a function should not be called.

t.exception(Promise|function|async function, [error], [message])

Verify that a function throws, or a promise rejects.

t.exception(() => { throw Error('an err') }, /an err/)
await t.exception(async () => { throw Error('an err') }, /an err/)
await t.exception(Promise.reject(Error('an err')), /an err/)

If the error is an instance of any of the following native error constructors, then this will still result in failure since native errors often tend to be unintentational.

  • SyntaxError
  • ReferenceError
  • TypeError
  • EvalError
  • RangeError

If a t.exception is async, then you're supposed to await it.

t.exception.all(Promise|function|async function, [error], [message])

Verify that a function throws, or a promise rejects, including native errors.

t.exception.all(() => { throw Error('an err') }, /an err/)
await t.exception.all(async () => { throw Error('an err') }, /an err/)
await t.exception.all(Promise.reject(new SyntaxError('native error')), /native error/)

The t.exception.all method is an escape-hatch so it can be used with the normally filtered native errors.

If a t.exception.all is async, then you're supposed to await it.

t.execution(Promise|function|async function, [message])

Assert that a function executes instead of throwing or that a promise resolves instead of rejecting. Resolves to the execution time, in milliseconds, of the function or promise.

t.execution(() => {})
await t.execution(async () => {})
await t.execution(Promise.resolve('cool'))

If a t.execution is async, then you're supposed to await it

t.is.coercively(actual, expected, [message])

Compare actual to expected with ==.

t.not.coercively(actual, expected, [message])

Compare actual to expected with !=.

t.alike.coercively(actual, expected, [message])

Object comparison, comparing all primitives on the actual object to those on the expected object using ==.

t.unlike.coercively(actual, expected, [message])

Object comparison, comparing all primitives on the actual object to those on the expected object using !=.

Utilities

t.plan(n)

Constrain a test to an explicit amount of assertions.

t.teardown(function|async function, [options])

Available options for teardowns:

  • order (0) - set the ascending position priority for a teardown to be executed.

The function passed to teardown is called right after a test ends:

test('basic', function (t) {
  const timeoutId = setTimeout(() => {}, 1000)

  t.teardown(async function () {
    clearTimeout(timeoutId)
    await doMoreCleanUp()
  })

  t.ok('cool')
})

If teardown is called multiple times in a test, every function passed will be called after the test ends:

test('basic', function (t) {
  t.teardown(doSomeCleanUp)

  const timeoutId = setTimeout(() => {}, 1000)
  t.teardown(() => clearTimeout(timeoutId))

  t.ok('again, cool')
})

Set order: -Infinity to always be in first place, and vice versa with order: Infinity.
If two teardowns have the same order they are ordered per time of invocation within that order group.

test('teardown order', function (t) {
  t.teardown(async function () {
    await new Promise(r => setTimeout(r, 200))
    console.log('teardown B')
  })

  t.teardown(async function () {
    await new Promise(r => setTimeout(r, 200))
    console.log('teardown A')
  }, { order: -1 })

  t.teardown(async function () {
    await new Promise(r => setTimeout(r, 200))
    console.log('teardown C')
  }, { order: 1 })

  t.pass()
})

The A teardown is executed first, then B, and finally C due to the order option.

t.timeout(ms)

Fail the test after a given timeout.

t.comment(message)

Inject a TAP comment into the output.

t.end()

Force end a test.
end is determined by assert resolution or when a containing async function completes.
In case of inverted tests, they're required to be explicitly called.

Runner

Default timeout

The default timeout is 30 seconds.

Example of package.json with test script

The following would run all .js files in the test folder:

{
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "test": "brittle test/*.js"
  },
  "devDependencies": {
    "brittle": "^3.0.0-alpha.3"
  }
}

CLI

npm install -g brittle@next
brittle [flags] [<files>]

Flags:
  -cov, --coverage              Turn on coverage
  --cov-dir <dir>               Configure coverage output directory (default: ./coverage)
  --bail                        Bail out on first assert failure
  --solo                        Engage solo mode
  -r, --runner <out> <targets>  Generates an out file that contains all target tests

Note globbing is supported:

brittle --coverage path/to/test/*.js

Auto generate a single file containing "all tests":

brittle -r test/all.js test/*.js

node test/all.js

You can use an environment variable to also set flags:

BRITTLE="--coverage --bail" brittle test.js

Force disable coverage with an environment variable:

BRITTLE_COVERAGE=false brittle test.js

Coverage

If the --coverage flag is set, brittle will output the coverage summary as a table at the end of execution and generate a json coverage report in the coverage output directory (configurable using --cov-dir).

The coverage output directory will contain a coverage-final.json file which contains an istanbul json coverage report and a v8-coverage.json file which contains the raw v8 coverage data.

Istanbul can be used to convert the istanbul json report into other formats. e.g.:

npx istanbul report html

License

Apache-2.0