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 🙏

© 2026 – Pkg Stats / Ryan Hefner

mocha-behavior-tree

v1.0.2

Published

Hierarchical step definition for Mocha

Readme

mocha-behavior-tree

Hierarchical step definition for Mocha

NPM License

Easily defining multiple next steps to test from the current state with a tree structure hierarchically. This is useful for performing tests in a natural sequence of actions and reducing duplication of test code.

Install

npm i -D mocha mocha-behavior-tree

Usage

const { step } = require('mocha-behavior-tree')

describe('test',                                   //Mocha's describe
  step('root step', () => foo,                      //root : passing value to next step
    step('step 1', foo => { },                      //depth1 : bypass the value when return nothing
      step('step 1.1', foo => { /* assertion */ }),   //depth2
      step('step 1.2', async foo => await bar,       //depth2 : async step
        step('step 1.2.1', bar => { /* assertion */ }), //depth3
        ...
      ),       
      ...
    )
  )    
)

Scenario, Given, When, Then

scenario is proxy for describe, and given,when,then,and are proxies for step, just adding BDD style keywords as prefix to the step name.

Below test case

const assert = require('assert')
const { scenario, given, and, then, when } = require('mocha-behavior-tree')

scenario('User purchases products',
  given('User login',                         () => console.log('1. User login'),
    and('List products',                        () => console.log('2. List products'),
      when('Add the first product to cart',       () => console.log('3. Add the first product to cart'),
        then('The product is in the cart',          () => assert(true))
      ),
      when('Puchase the first products',          () => console.log('3. Puchase the first products'),
        then('An order for the product is created', () => assert(true))
      ),
      when('Select all products on the page',     () => console.log('3. Select all products on the page'),
        then('all products are selected',           () => assert(true)),
        and('Add selected products to cart',        () => console.log('4. Add selected products to cart'),
          then('The products are in the cart',        () => assert(true))
        ),
        and('Puchase selected products',            () => console.log('4. Puchase selected products'),
          then('Orders for the products are created', () => assert(true))
        ),
      ),
    )
  )
)

is executed as follows:

// running each step route of the trees from the root to the leaf

  Scenario: User purchases products
    Given: User login
      And: List products
        When: Add the first product to cart
          1. User login
          2. List products
          3. Add the first product to cart
          ✓ Then: The product is in the cart
          
        When: Puchase the first products
          1. User login
          2. List products
          3. Puchase the first products
          ✓ Then: An order for the product is created
          
        When: Select all products on the page
          1. User login
          2. List products
          3. Select all products on the page
          ✓ Then: all products are selected
          
          And: Add selected products to cart
            1. User login
            2. List products
            3. Select all products on the page
            4. Add selected products to cart
            ✓ Then: The products are in the cart
            
          And: Puchase selected products
            1. User login
            2. List products
            3. Select all products on the page
            4. Puchase selected products
            ✓ Then: Orders for the products are created

  5 passing 

For integration with other test codes (or some IDE plugins), you can also do this:

describe('Shop tests', () => {
  describe('User', () => {
    // Note that the scenario is called 'inside' the callback function, 
    // not passed to describe as a callback function.
    scenario('User purchases products', 
      given('User login', () => { /*...*/ },
        /*...*/
      )
    )
  })
})

Passing values to the next Step

Multiple values can be passed to next step within object key-values

describe('test multiple value passing',
  step('pass 1, 2', 
    () => ({ a:1, b:2 }),
    step('passed value is 1, 2', 
      ({a, b}) => {
        expect(a).to.equals(1)
        expect(b).to.equals(2)
      }
    )
  )
)

Async testing

when('products are listed in the page', 
  () => getProductsPage({ page: 1, size: 20 }).then(result => ({ page: result })), // returns promise
  then('Add the first product to cart', 
    ({ page }) => {
      // got resolved value here
    }
  )
)
when('products are listed in the page', 
  async () => ({ page : await getProductsPage({ page: 1, size: 20 }) }), // async/await
  then('Add the first product to cart', 
    ({ page }) => {
      // got resolved value here
    })
)

Use function name as step name

Step name can be omitted. then function name will be used to step name

describe('User purchases products',
  given(function user () { 
      //... 
    },
    and(function user_logged_in (user) { 
        //... 
      },
      and(function products_are_listed_in_the_page (user) { /*...*/ },
        when(function add_the_first_product_to_cart ({ user, page }) { /*...*/ },
          then(function the_product_is_in_the_cart ({ cart, firstProduct }) { /*...*/ })
        )
      )
    )
  )
)

result :

User purchases products
  Given: user
    And: user_logged_in
      And: products_are_listed_in_the_page
        When: add_the_first_product_to_cart
          ✓ Then: the_product_is_in_the_cart

This can be done more Cucumber way

// steps.js
module.exports = {
  'user': () => { ... },
  'user logged in': () => { ... },
  'products are listed in the page': () => { ... },
  'add the first product to cart': () => { ... },
  'the product is in the cart': () => { ... },
}

//test.js
const _ = require('./steps')
describe('User purchases products',() => {
  given(_['user'],
    and(_['user logged in'],
      and(_['products are listed in the page'],
        when(_['add the first product to cart'],
          then(_['the product is in the cart'])
        )
      )
    )
  )()
})

More usages

You can find out more examples in the library's test cases