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

promise-puppeteer

v1.1.0

Published

Easy to use JS promise test double/fake/mock and execution analysis library

Downloads

2

Readme

Promise Puppeteer

Table Of Contents

Introduction

Promise Puppeteer is a drop-in test solution for handling ES-Next and Promise/A+ promises in your code under test. The goal of Promise Puppeteer is to make it as smooth and easy as possible to test code consuming promises without having to do a bunch of test gymnastics.

Why use Promise Puppeteer:

  • Easy to use -- API is exposed to developer to allow for full control over promise execution
  • Predictable -- Executes promise code as it behaves in the wild, making it easier to verify everything works as expected
  • Deterministic execution -- Your test is separated from the world: resolve or reject from within tests
  • Clear communication -- Built to fail when code acts unpredictably: Resolve and reject throw when called twice; Optionally throws an error if no catch or onFailure behavior is registered (default: on)
  • Easy execution analysis -- Optional step-by-step execution of resolve behavior for easier analysis

Compatibility:

  • ES-Next Promise Standard:
    • Promise
      • all
      • race
      • new Promise()
    • Thenable
      • then
      • catch
      • finally
  • Promise/A+
    • then supports both onSuccess and onFailure optionally

Promise Puppeteer works both in Node and client-side code, so it integrates seamlessly into all of your test scenarios. With its clear, simple API, it is easy to either execute through the entire code stack or perform step-by-step evaluation of promise resolution.

Setup

Setup is as simple as performing an NPM installation and including the library in your test environment:

npm install promise-puppeteer --save-dev

The library exists at the following path:

project-root/node_modules/promise-puppeteer/index.js

That's it!

Test Examples

A common test example

A common test setup scenario would look like the following (using the Mocha test framework):

const sinon = require('sinon');

const promiseDoubleFactory = require('promise-puppeteer');
const moduleUnderTestFactory = require('./moduleUnderTestFactory');

describe('Module Under Test', function () {

    let thenableFake;
    let moduleUnderTest;

    beforeEach(function () {
        thenableFake = promiseDoubleFactory.getThenableFake();

        const MyService = require('./MyService');
        sinon.stub(MyService, 'doAsyncThing').callsFake(() => thenableFake);

        moduleUnderTest = moduleUnderTestFactory(MyService);
    });

    it('consumes a promise', function() {
        // Call method under test
        moduleUnderTest.doSomethingAsync();

        // Initiate promise resolution, firing all thens, catches and finallys
        thenableFake.resolve({ foo: 'bar' });

        // perform test assertion around result from doSomethingAsync
    });

});

A running thenable fake

An example of consuming a Promise Puppeteer thenable straight from the actual test suite is as follows:

const thenableFake = getThenableFake();

const thenStub = sinon.stub();

thenableFake
    .then(value => value + 1)
    .then(value => value * 2)
    .then(thenStub)
    .catch(() => null)
    .resolve(5);

assert.equal(thenStub.args[0][0], 12);

It's worth noting thenableFake adheres to the ES-Next promise standard, with the addition of resolve, which will execute the promise as if it were resolved from an async executor (action).

Step-wise then resolution

You can step through a promise resolution with the resolveNext function. The following execution allows the user to analyze what happens as each then executes and exits.

const thenableFake = getThenableFake();

const thenStub = sinon.stub();

thenableFake
    .then(value => value + 1)
    .then(value => value * 2)
    .then(thenStub)
    .catch(() => null);

const result1 = thenableFake.resolveNext(5); // runs the first 'then'
const result2 = thenableFake.resolveNext(result1); // runs the second 'then'
const result3 = thenableFake.resolveNext(result2); // runs the third 'then'

assert.equal(thenStub.args[0][0], result3);
assert.equal(result3, 12);

Rejecting a thenable fake

Rejecting a thenable fake will work exactly as expected, executing no then onSuccess functions, jumping straight to the catch.

const thenableFake = getThenableFake();

const thenStub = sinon.stub();
const catchStub = sinon.stub();

thenableFake
    .then(value => value + 1)
    .then(value => value * 2)
    .then(thenStub)
    .catch(() => null)
    .reject(new Error('Because.'));

assert.equal(thenStub.callCount, 0);
assert.equal(thenStub.args[0][0].message, 'Because.');

PromiseFake and Thenable APIs

PromiseFake API

// Getting a new instantiable PromiseFake -- required for test state safety
const PromiseFake = promiseDoubleFactory.getPromiseFake();

// Promise.all
const thenableFake = PromiseFake.all([ /* these promises are not processed. */ ]);

// Promise.race
const thenableFake = PromiseFake.race([ /* these promises are not processed. */ ]);

// Instantiation
const thenableProxyFake = new PromiseFake(function(resolve, reject){
    // Code goes here
    // Resolving and rejecting DOES NOT initiate actual promise resolution
});

// Available methods on thenableProxyFake:
thenableProxyFake.then();
thenableProxyFake.catch();
thenableProxyFake.finally();
thenableProxyFake.disableThrowOnNoCatch();

// Access to internal values:
const resolveArguments = thenableProxyFake.resolve.args;
const rejectArguments = thenableProxyFake.reject.args;

Thenable Fake API

// Getting a new thenable fake object
const thenableFake = promiseDoubleFactory.getThenableFake();

// Chaining thens, catches and finallys:
thenableFake
    .then(onSuccess1, onFailure1)
    .then(onSuccess2)
    .catch(onFailure2)
    .catch(onFailure3)
    .finally(onComplete1)
    .finally(onComplete2);

// Turning off errors when catch is missing on a thenable fake
thenableFake.disableThrowOnNoCatch();

// Resolve and execute onSuccess and onComplete functions completely:
thenableFake.resolve(arg1, arg2, ...);

// Reject and execute onFailure and onComplete functions completely:
thenableFake.reject(new Error('Something bad happened'));

Special Case and Analysis Step-wise Resolution

// Verify resolveNext can be called safely:
thenableFake.canResolve();

// Resolve onSuccess functions incrementally, calling onComplete functions upon last onSuccess execution completion:
const outcome = thenableFake.resolveNext(arg1, arg2, ...); // Returns outcome from internal execution

Version History

v1.0.0

Initial release:

  • PromiseFake
    • all
    • race
  • ThenableFake
    • then

    • catch

    • finally

    • resolve

    • resolveNext

    • reject