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

testesterone

v1.1.1

Published

In-browser test runner and assertion library

Downloads

11

Readme

Testesterone

Strengthen your clientside applications with in-browser tests

  • No need to mock window and browser APIs
  • No global scope pollution
  • Output is printed to the console
  • Async tests are supported
  • Minimal expect-style assertions are built-in

Installation

npm install testesterone -D

Example

index.html

<h1>Hello!</h1>
<input type="text" placeholder="Your name" id="nameInput">

app.js

var h1 = document.querySelector('h1');
var nameInput = document.getElementById('nameInput');

nameInput.oninput = function() {
  h1.textContent = 'Hello ' + nameInput.value;
};

app.test.js

var test = require('testesterone');

var h1 = document.querySelector('h1');
var nameInput = document.getElementById('nameInput');

test('Hello world', function(it) {

  it('is initialized to "Hello"', function(expect) {
    expect(h1.textContent).to.equal('Hello!');
  });

  it('updates text to input\'s value', function(expect) {
    nameInput.value = 'world';
    nameInput.oninput();
    expect(h1.textContent).to.equal('Hello world');
  });

  it('saves result in localstorage'); // not yet implemented!

})();

Overview

Testesterone was built to make it simple to test clientside code within a browser. This means there is no need for headless browsers, or for simulating a browser environment from within node.js.

In addition, all test results appear in the console, not in any sort of ui on the page itself. This allows you to run assertions against the actual contents of the DOM within your tests.

API

test(label, callback)

The test function creates a group in the console that contains your assertions.

test('foo', function() {
  // ...
})();

test is the equivalent to Mocha's describe function.

tests can also be nested, which allows for grouping. For example:

test('Array', function() {
  test('indexOf', function() {});
  test('join', function() {});
  test('push', function() {});
  test('slice', function() {});
})();

Note that the outermost test()'s return value is a function that must be called! Without doing this, your tests will not be run. ie. You should do this: test(label, callback)()

Of course, this is not very useful without assertions to run.

it(label, callback)

Assertions are run within the context of a test case defined by an it function.

test('foo', function(it) {
  it('runs a test');
})();

it(label) Produces an informational label that does not run any assertions. This can be a placeholder for some future test that needs to be written, or for some functionality that has yet to be implemented.

it(label, callback) Calls the callback and runs its assertions. If no assertion error occurred, then the test will pass.

test('foo', function(it) {
  it('runs a test', function(expect) {
    expect(123).to.equal(456);
  });
})();

This test obviously fails. We can expand the failing test case to see the reason for the failure. And in fact, this can be further expanded to reveal the full stack trace if desired.

Now let's make the test pass:

test('foo', function(it) {
  it('runs a test', function(expect) {
    expect(123).to.equal(123);
  });
})();

Or for a more complete example:

test('Array', function(it) {

  test('indexOf', function() {
    it('returns the index of an element in the array', function(expect) {
      expect([1, 2, 3].indexOf(3)).to.equal(2);
    });
  });

  test('join', function() {
    it('joins an array into a string', function(expect) {
      expect([1, 2, 3].join(':')).to.equal('1:2:3');
    });
    it('joins with a comma by default', function(expect) {
      expect([1, 2, 3].join()).to.equal('1,2,3');
    });
  });

  test('push', function() {
    it('appends an item to an array', function(expect) {
      var x = [1, 2, 3];
      x.push(4);
      expect(x).to.equal([1, 2, 3, 4]);
    });
  });

})();

All the tests pass, but one. The last test fails because x is not strictly equal to the new array [1, 2, 3, 4] although they are structurally the same. The expect function comes with a built-in deep equality detector for such situations. Changing expect(x).to.equal([1, 2, 3, 4]); to expect(x).to.deep.equal([1, 2, 3, 4]); will solve this problem.

expect()

Expect has a very minimal assertion API. It has the following methods:

  • expect(x).equal(y): asserts that x === y
  • expect(x).not.equal(y): asserts that x !== y
  • expect(x).deep.equal(y): asserts that deepEquals(x, y)
  • expect(x).not.deep.equal(y): asserts that !(deepEquals(x, y))
  • expect(x).explode(): asserts that x() throws an error
  • expect(x).not.explode(): asserts that x() does not throw

All of the methods can be prefixed with to so that they read like spoken english. For example:

expect(x).to.equal(y);

Unlike assertion libraries like Chai, which include many methods that allow code like the following to be written: expect('foo bar').to.include('bar');, this library opts instead for a minimal api so that you do not have to check the documentation to know what is and is not supported by the assertion library. Anything that could be tested with those helper methods can be tested using the to.equal() interface. For instance, the previous example could be written as: expect('foo bar'.includes('bar')).to.equal(true); or even:

expect('foo bar'.indexOf('bar') > -1).to.equal(true);

Async support

Async functions can be easily tested with testesterone. As a bonus, their executions will be timed. In order to test any asynchronous code, pass a second parameter to the it() function. That second parameter will be a callback that should be called when the asynchronous code is finished executing.

test('foo', function(it) {
  it('runs a test after 1 second', function(expect, done) {
    setTimeout(function() {
      expect('foo').to.equal('foo');
      done();
    }, 1000);
  });
})();

All tests, including asynchronous tests, will be run sequentially.

This works with promises as well. The equivalent promise to the above function would be:

test('foo', function(it) {
  it('runs a test after 1 second', function(expect, done) {
    new Promise(function(resolve) {
      setTimeout(function() {
        expect('foo').to.equal('foo');
        resolve();
      }, 1000);
    }).then(done);
  });
})();

If done is not called within 2 seconds of the test being run, the test will automatically fail. This timeout can be configured by setting test.timeout to any number of milliseconds prior to running a test. The timeout can even be modified on a test-by-test basis.

For example:

test.timeout = 9000; // set the max time a single test can run to 9 seconds

test('foo', function(it) {
  it('tests a long-running function', function(expect, done) {
    setTimeout(function() {
      expect('foo').to.equal('foo');
      done();
    }, 4000);
  });
})();

And it should be noted that tests do not have to be asynchronous in order for the done callback to be useful. For heavy functions that may take a while to compute, the timer provided by calling done() may be nice to have. It relies internally on performance.now().

test('heavy function', function() {
  it('takes a while to compute', function(expect, done) {
    var numbers = [];
    for (var i = 0; i < 123456789; i++) {
      numbers.push(i);
    }
    done();
  });
});

Shorthand

Assertions can be written in a more terse syntax if desired:

test('foo', function(it) {
  it('does something', (t) => t('foo').not.equal('bar')); // test passes
  it('does something else', (t) => t('foo').equal('bar')); // test fails
});

Theme support

Testesterone also works with the dark theme in Chrome's developer console:

Caveats

  1. Browsers besides Google Chrome are not officially supported at this time. Many do not have the same degree of support for the console methods, which this library makes use of.
  2. Tests must be run within a browser context. This wasn't intended to be used in node.js.
  3. The expect interface relies internally on console.assert and does not throw an error if an assertion fails. For this reason, external assertion libraries like Chai would be difficult to integrate.
  4. You must have a single call to test(label, callback)() at the outermost level. Multiple tests can be side-by-side within the callback, but there can only be one root-level call to test.