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

polymorphic-tests

v0.2.6

Published

Polymorphic test framework for JS by Wizard Enterprises.

Readme

Polymorphic test framework for JS by Wizard Enterprises.

🏠 Homepage

See also polymorphic-web-component-tests.

Setting Up

Install with npm i -D polymorphic-tests or yarn add -D polymorphic-tests.

Once polymorphic-tests is installed, run ./node_modules/.bin/polytest init to configure your project.

This tool depends on stage-0 decorators, implemented through either Babel or TypeScript. This is configured automatically through the init command, but configuring TypeScript or Babel is left to the user (see examples).

Running is done simply through ./node_modules/.bin/polytest run. This can be added to your npm scripts (and is during init, if it's not defined) for added convenience.

Configuring

The init command generates a polytest.js file in your project, which exports configuration for running your tests. See ./node_modules/.bin/polytest help run for nearly full options.

Nearly because the run --setup flag only accepts globs to setup files, while the polytest.js#setup field can additionally accept plain old functions to run during setup.

Writing Tests

This section assumes you understand polymorphism through class extension in JS.

Simple Test

Tests are written in classes extending TestSuite, and must both extend TestSuite and be decorated with the @Suite() decorator, for reasons that will be explained below.

Note: test methods may always return a promise to be awaited.

import {Test, Suite, TestSuite} from 'polymorphic-tests'

@Suite() class MySuite extends TestSuite {
  @Test() '1 + 1 = 2'(t) {
    t.expect(add(1, 1).to.equal(2)
  }
}

function add(x, y) {
  return x + y
}

t.expect here is from the Chai Assertion Library. t also includes Chai's should and assert APIs. Chai can be configured further by configuring setup, see Configuring.

Sub-Suites

Test suites can contain other test suites.

import {Test, Suite, SubSuite, TestSuite} from 'polymorphic-tests'

@Suite() class Calculator extends TestSuite {
  @Test() 'calculator exists'(t) {
    t.expect(calculate).to.be.an.instanceof(Function)
  }
}

@SubSuite(Calculator) class Add extends TestSuite {
  @Test() '3 + 3 = 6'(t) {
    t.expect(calculate('+', 3, 3)).to.equal(6)
  }
}

@SubSuite(Calculator) class Multiply extends TestSuite {
  @Test() '3 * 3 = 9'(t) {
    t.expect(calculate('*', 3, 3)).to.equal(9)
  }
}

function calculate(operation: string, x: number, y: number) {
  return eval(x + operation + y)
}

Note that sub-suites still extend TestSuite and not their parent suites! This is be explained below.

This is mostly useful for reporting and for test selection and filtering.

Test Selection and Filtering

The @Suite, @SubSuite, and @Test decorators can all recieve as their last argument an options config with flags skip and only, e.g. @Test({skip: true}), @SubSuite(MyParentSuite, {only: true}).

The only flag is not global. This means decorating a test entity with it only applies inside that entity's suite. Currently there is no global only flag.

The skip flag skips the decorated test entity and any children it might have, taking complete priority over only.

So for example:

import {Test, Suite, SubSuite, TestSuite} from 'polymorphic-tests'

@Suite() class RootWithEntitiesWithFlags extends TestSuite {
  @Test() 'skipped because of sibling suite with only'(t) {}
}

@SubSuite(RootWithEntitiesWithFlags) class SkippedBecauseOfSiblingWithOnly extends TestSuite {}

@SubSuite(RootWithEntitiesWithFlags, {only: true}) class SuiteWithOnly extends TestSuite {
  @Test() 'runs normally'(t) {}
  @Test({skip: true}) 'still skipped'(t) {}
}

@Suite({skip: true}) class SkippedRoot extends TestSuite {}

@Suite() class UnaffectedRoot extends TestSuite {
  @Test() 'runs normally'(t) {}
}

Lifecycle Hooks

Every test suite exposes these hook methods for extending. It's good practice to always call super.lifecycleHook() when you override one of these.

Note: hook methods may always return a promise to be awaited.

import {Test, Suite, TestSuite} from 'polymorphic-tests'

@Suite() class MySuite extends TestSuite {
  static onDecorate() {
    // called when MySuite gets decorated
  }

  setup() {
    // called once when MySuite starts running
  }

  before(t) {
    // called before every test
  }

  @Test() 'some test'(t) {}

  after(t) {
    // called after every test
  }

  teardown() {
    // called once when MySuite ends
  }
}

Hooks still run after a test fails. This is important for cleanup purposes, but be aware that if an error gets thrown during after and teardown it will be reported and your test error will be swallowed.

Extending TestSuite

Test suites being implemented as classes allows sharing all sorts of common boilerplate very easily through polymorphically implementing lifecycle hooks. For a full-fledged example of this, see polymorphic-web-component-tests.

Another use for extending TestSuite is inheriting tests, in addition to boilerplate.

import {Test, Suite, TestSuite} from 'polymorphic-tests'

abstract class CustomTestSuite extends TestSuite {
  abstract testString: string
  abstract expectedLength: number

  @Test() 'my test'(t) {
    t.expect(this.testString.length).to.equal(expectedLength)
  }
}

At this point, by design, no tests will run. This is because CustomTestSuite wasn't decorated with the Suite or SubSuite decorators for registration, so our test has no parent. This allows us to do this:

import {Test, Suite} from 'polymorphic-tests'
import {CustomTestSuite} from '...'

@Suite() class FirstSuite extends CustomTestSuite {
  testString = 'foo'
  expectedLength = 3
}

@Suite() class SecondSuite extends CustomTestSuite {
  testString = 'longer'
  expectedLength = 6
}

Note that here, FirstSuite#my test and SecondSuite#my test will run, but CustomTestSuite#my test will never run.

Author

👤 [email protected]

🤝 Contributing

Contributions, issues and feature requests are welcome!Feel free to check issues page.

Show your support

Give a ⭐️ if this project helped you!

📝 License

Copyright © 2019 [email protected]. This project is ISC licensed.