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

suite-metrics

v2.5.0

Published

Easily keep track of metrics for many nested test suites

Downloads

3,578

Readme

suite-metrics

npm npm npm Buy Me A Coffee

Easily track and aggregate test timing metrics for many nested test suites

Features:

  • Precision Tracking: Measure test execution time down to microseconds
  • Flexible Nesting: Organize tests in any number of nested suites with any structure
  • Comprehensive Metrics: Get aggregate test data, find outliers, and perform statistical tests
  • Easy Interface: Simple methods calls provided with clear documentation
  • Concurrency Support: Allows for tracking of multiple concurrent tests safely

📦 Installation

npm i suite-metrics -D

# or
pnpm i suite-metrics -D

# or
yarn add suite-metrics -D

🌐 CDN Usage

You can also use suite-metrics directly in the browser via CDN:

<!-- Using unpkg -->
<script src="https://unpkg.com/suite-metrics"></script>

<!-- Using jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/suite-metrics"></script>

<!-- Specify version (recommended for production) -->
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>

When loaded via CDN, the library is available globally:

<script>
  // Same usage as below
  const metrics = new SuiteMetrics();
  
  metrics.startTest(["Suite Name", "Test Name"]);
  // Test logic...
  metrics.stopTest();
  
  console.log(metrics.performance.getSlowestTest());
</script>

🚀 Usage

Setup

If you are not running tests concurrently, use SuiteMetrics:

import SuiteMetrics from 'suite-metrics';

// Use as a lazy singleton for easy access across multiple files
const metricsSingleton = SuiteMetrics.getInstance();

// Alternatively, create a new instance for isolated metrics
const metrics = new SuiteMetrics();

For running multiple tests concurrently, ConcurrentSuiteMetrics is required:

import { ConcurrentSuiteMetrics } from 'suite-metrics';

// Lazy singleton is also available, but is async to prevent multiple allocations
const concurrentMetricsSingleton = await ConcurrentSuiteMetrics.getInstance();
// or
const concurrentMetrics = new ConcurrentSuiteMetrics();

// Optionally set the start/stop test mutex timeout (default: 100ms)
const concurrentMetricsWithCustomMutexTimeout = new ConcurrentSuiteMetrics(10);

Note: ConcurrentSuiteMetrics works fine for sequential tests; however, it is more complex (async calls, a parameter for stopTest(), and some mutex lock overhead) so it is recommended to only use it when required

Tracking Tests

Standard SuiteMetrics is simple:

// Start tracking a test (directly before test logic for best accuracy)
metrics.startTest(["Suite Name", "Sub-suite name", "Test Name"]);

// Execute your test logic here...

// Call directly after test logic completes to stop tracking
metrics.stopTest();

Concurrent metrics can run multiple at the same time:

const promises = [
    (async () => {
        const testName = ["Suite Name", "Test Name 1"];
        await concurrentMetrics.startTest(testName);
        // Test logic...
        await concurrentMetrics.stopTest(testName);
    })(),
    (async () => {
        const testName = ["Suite Name", "Test Name 2"];
        await concurrentMetrics.startTest(testName);
        // Test logic...
        await concurrentMetrics.stopTest(testName);
    })(),
    (async () => {
        const testName = ["Suite Name", "Test Name 3"];
        await concurrentMetrics.startTest(testName);
        // Test logic...
        await concurrentMetrics.stopTest(testName);
    })()
];

await Promise.all(promises);

Getting Test Data

Note: All internal returned data (Tests, Suites, arrays) are frozen to prevent accidental environment corruption

Both SuiteMetrics and ConcurrentSuiteMetrics have extensive methods in composite classes:

  • BaseSuiteMetrics: Base class with straightforward methods like validatePath() and getAllData()
  • queries: Query for Suites and Tests, such as getTest() and suiteExists()
  • metrics: Gets aggregate metrics for single or multiple suites
  • performance: Gets the fastest or slowest test(s) in order
  • statistics: Helpers for Z-scores and standard deviation

⚠️ Important ⚠️: If you are using ConcurrentSuiteMetrics, these methods are NOT thread-safe. Do not call while concurrently running tests.

BaseSuiteMetrics

metrics.validatePath(["Suite 1", "Test 1"], true); // -> true if that test exists

metrics.pathToString(['suite 1', 'sub-suite 2', 'test 3']); // -> "[suite 1, sub-suite 2, test 3]"

metrics.getTestsInOrder(); // -> all tests in the order they were completed

metrics.getAllData(); // -> all data in this metrics as-is

metrics.toJSONString(); // -> all data in this metrics, serialized to JSON

queries

// Check if a suite or test exists
if (metrics.queries.suiteExists(["Suite Name"])) {
    // ...
}

if (metrics.queries.testExists(["Suite Name", "Test Name"])) {
    // ...
}


// Gets a full Suite or Test object
metrics.queries.getSuite(["Suite 1", "Sub-suite"]); // -> suite's name, tests, sub-suites, and aggregate data

metrics.queries.getTest(["Suite 1", "Sub-suite", "Test 1"]); // -> test's name, timestamps, and metadata


// Get child suite/test names
metrics.queries.getSuiteNames(["Suite 1"]); // -> all suite names directly in this suite

metrics.queries.getTestNames(["Suite 2"]); // -> all test names directly in this suite

metrics

metrics.metrics.getTotalTestCount(); // -> number of (completed) tests in this metrics instance

metrics.metrics.getAverageTestDuration(); // -> average test duration for all tests (microseconds)

metrics.metrics.getMedianTestDuration(); // -> median duration for all tests (microseconds)

metrics.metrics.getSuiteMetrics(["Suite Name"]); // -> suite's location and test metrics (direct and sub-suites)

console.log(metrics.metrics.printAllSuiteMetrics()); // -> human-readable summary of all tests

metrics.metrics.getStructureMetadata(); // -> high-level aggreagate summary (depth metrics, active time, test distribution, etc)

performance

metrics.performance.getSlowestTest(); // -> slowest test overall

metrics.performance.getKSlowestTests(5); // -> the 5 slowests tests overall, from slowest to fastest

metrics.performance.getAllTestsSlowestFirst(); // -> all tests, slowest first

metrics.performance.getFastestTest(); // -> fastest test overall

metrics.performance.getKFastestTests(10); // -> the 10 fastest tests overall, from fastest to slowest

metrics.performance.getAllTestsFastestFirst(); // -> all tests, fastest first

statistics

metrics.statistics.getStandardDeviation(); // -> standard deviation for all test times combined

metrics.statistics.getTestZScore(/* <test object> */); // -> Z-score for the test (e.g. 0.7)

metrics.statistics.getAllTestsWithZScores(); // -> every test with their Z-score

metrics.statistics.interpretZScore(2); // -> human-readable z score interpretation (e.g. see below)
result = {
    interpretation: 'Unusual performance',
    severity: 'unusual',
    description: 'Test is unusually slow'
}

⏱️ Performance & Time Complexity

Overview

This package uses lazy loading and caching to optimize performance, making most operations O(1) constant time.

Non-Constant Operations

| Operation | Complexity | Notes | |-------------------------------------|------------|-----------------------------------------------------------------------------------------------------------------------| | ConcurrentSuiteMetrics methods | O(k) | k = number of waiting operations. Very fast in practice (~few ms for 100 concurrent tests) | | Getting/Adding Suites/Tests | O(k) | k = depth of Suite/Test in hierarchy. Minimal for typical use cases | | Returning multiple Suites/Tests | O(k) | k = number of items returned, except getting all items after a cache rebuild has been performed | | Performance methods | O(n log n)O(k) | Cache rebuild when tests added, then O(k) for subsequent calls (returning k Tests) | | Statistics methods | O(n)O(1) | Cache rebuild (O(m) for m new tests) when tests added (except interpretZScore()), then O(1)/O(k) | | Data exporting | O(n) | Methods toJSONString(), printAllSuiteMetrics(), getStructureMetadata() and getAllData() require a full traverse |

⚡ Performance Best Practices

  • Test execution: Run all tests before gathering metrics to ensure cache rebuilds only run once
  • Suite depth: Keep suite hierarchies reasonable (avoid 100s of deeply nested suites)
  • Bulk operations: Minimize repeated calls to methods returning large datasets (10,000+ tests)

Real-World Performance

In typical scenarios, performance overhead is negligible due to efficient caching. For large cases (~10,000+ tests), following the recommended patterns above to reduce overhead.

📃 Changelog

To view the release notes for each version, view the changelog:

  • On GitHub: Link
  • On npm: package page -> CHANGELOG.md
  • In the repository: CHANGELOG.md

Buy me a coffee if this package helped you!