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

qapulsesk-report

v2.3.0

Published

All-in-one test reporter for Playwright, Cypress, Jest, Vitest, Puppeteer, Selenium & WebdriverIO — beautiful dark-theme HTML reports with screenshots on failure, AI failure analysis, Slack/Teams webhooks. By QAPulse by SK.

Readme

QAPulseSK-report

npm version npm downloads License: MIT Node.js QAPulse by SK

The only test reporter you'll ever need. Playwright · Cypress · Jest · Vitest · Puppeteer · Selenium · WebdriverIO — one package, zero config, beautiful results.

qapulsesk-report demo


✨ Why QAPulseSK-report?

Most teams install 4–5 separate reporter packages — one per framework, one for Slack, one for AI, one for trends. We ship everything in one.

| Feature | QAPulseSK-report | Others | |---|---|---| | 7 test runners in one package | ✅ | ❌ 1 per package | | Screenshots on failure (all runners) | ✅ | Partial | | Failure clustering (dedup similar errors) | ✅ | ❌ | | 7 built-in themes | ✅ | ❌ | | Trend + sparkline + histogram + timeline | ✅ | Paid | | Diff vs previous run + failure state badges | ✅ | ❌ | | Auto git/CI metadata (branch, commit, PR link) | ✅ | ❌ | | AI failure analysis | ✅ Opt-in, your key | ❌ | | Slack + Teams + Discord webhooks | ✅ Built-in, rich | Basic | | JSON export | ✅ | ❌ | | Zero cost to use | ✅ Always | Often paid |


🚀 Install

npm install qapulsesk-report --save-dev

Node 18+ required.


📖 Quick Start — Per Runner

Playwright

// playwright.config.ts
import { defineConfig } from '@playwright/test';
import path from 'path';

export default defineConfig({
  use: { screenshot: 'only-on-failure' },   // <- enables screenshot capture
  reporter: [
    ['list'],
    [
      path.resolve(__dirname, 'node_modules/qapulsesk-report/dist/adapters/playwright.js'),
      {
        outputDir:   'qapulse-report',
        reportTitle: 'My E2E Tests',
        history:     { enabled: true },
      }
    ],
  ],
});

Playwright screenshots ship natively — no extra hooks needed.

Cypress

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  reporter: 'node_modules/qapulsesk-report/dist/adapters/cypress.js',
  reporterOptions: {
    outputDir:   'qapulse-report',
    reportTitle: 'My Cypress Tests',
    history:     { enabled: true },
  },
  e2e: {
    screenshotOnRunFailure: true,   // <- default true, keep it on
    setupNodeEvents(on, config) { return config; }
  }
});

Jest

// jest.config.js
module.exports = {
  reporters: [
    'default',
    ['qapulsesk-report/jest', {
      outputDir:   'qapulse-report',
      reportTitle: 'My Jest Tests',
      history:     { enabled: true },
    }],
  ],
};

For screenshots (Jest + Puppeteer):

// jest.setup.js
const { attachScreenshot } = require('qapulsesk-report');

afterEach(async () => {
  const state = expect.getState();
  if (state.assertionCalls > 0 && state.numPassingAsserts < state.assertionCalls) {
    const p = `/tmp/${Date.now()}.png`;
    await page.screenshot({ path: p });
    attachScreenshot(state.currentTestName, p);
  }
});

Vitest

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { QAPulseVitestReporter } from 'qapulsesk-report/vitest';

export default defineConfig({
  test: {
    reporters: [
      'verbose',
      new QAPulseVitestReporter({
        outputDir:   'qapulse-report',
        reportTitle: 'My Vitest Tests',
        history:     { enabled: true },
      }),
    ],
  },
});

Screenshots via attachScreenshot (same as Jest above).

Puppeteer (standalone, no runner)

import puppeteer from 'puppeteer';
import { QAPulsePuppeteerReporter } from 'qapulsesk-report/puppeteer';

const reporter = new QAPulsePuppeteerReporter({
  outputDir:   'qapulse-report',
  reportTitle: 'Smoke Suite',
  history:     { enabled: true },
});

const browser = await puppeteer.launch();
const page = await browser.newPage();

reporter.startTest('Home page loads', { suite: 'Public' });
try {
  await page.goto('https://example.com');
  await page.waitForSelector('h1');
  reporter.endTest('passed');
} catch (err) {
  const shot = `/tmp/${Date.now()}.png`;
  await page.screenshot({ path: shot });
  reporter.endTest('failed', { error: err, screenshotPath: shot });
}

await browser.close();
await reporter.finish();

Selenium (standalone)

import { Builder } from 'selenium-webdriver';
import { QAPulseSeleniumReporter } from 'qapulsesk-report/selenium';
import * as fs from 'fs';

const reporter = new QAPulseSeleniumReporter({ reportTitle: 'Selenium Suite' });
const driver = await new Builder().forBrowser('chrome').build();

reporter.startTest('Login works', { suite: 'Auth' });
try {
  await driver.get('https://example.com/login');
  reporter.endTest('passed');
} catch (err) {
  const shot = await driver.takeScreenshot();
  const p = `/tmp/${Date.now()}.png`;
  fs.writeFileSync(p, shot, 'base64');
  reporter.endTest('failed', { error: err, screenshotPath: p });
}

await driver.quit();
await reporter.finish();

WebdriverIO

// wdio.conf.ts
import QAPulseWDIOReporter from 'qapulsesk-report/webdriverio';

export const config = {
  reporters: [
    [QAPulseWDIOReporter, {
      outputDir:   'qapulse-report',
      reportTitle: 'WDIO Suite',
      history:     { enabled: true },
    }],
  ],
};

For screenshots, add an afterTest hook that calls attachScreenshot(test.fullTitle, path).


📊 What the Report Shows

Every report is a single portable HTML file with:

  • Metadata bar — auto-detected git branch, commit, author, PR link, CI job link
  • Diff banner+N new · −N recovered · N still failing vs the previous run
  • Pass rate ring + sparkline — at-a-glance status and trajectory across runs
  • Stats cards — passed / failed / skipped / duration / total
  • 🧩 Failure clusters — similar errors grouped by normalized signature
  • 🔍 Insights
    • Suite health matrix (heatmap of per-suite pass rate)
    • Duration distribution histogram
    • Top 10 slowest tests
    • Per-suite execution timeline (colored strip)
  • 📈 Trend chart — pass rate + passed/failed counts over your last N runs
  • ❌ Failed tests — expandable rows with error, stack, screenshots, AI analysis, and failure-state badge (🆕 new / 💥 regression / 🔁 recurring)
  • 🧪 All test suites — every test with its status

Plus a companion qapulse-report.json for downstream tools.


🎨 Themes

Seven built-in presets — set with a single line:

theme: { name: 'dracula' }

| Name | Look | |---|---| | qapulse-dark (default) | Deep blue-black, blue accent | | qapulse-light | Warm off-white, blue accent | | github-dark | GitHub UI dark palette | | github-light | GitHub UI light palette | | dracula | Dracula editor theme | | solarized-light | Solarized parchment | | minimal | Pure monochrome |

Override any color inline:

theme: { name: 'qapulse-dark', primaryColor: '#00ffcc' }

📷 Screenshots on Failure

Handled per runner:

| Runner | How screenshots arrive | |---|---| | Playwright | Native — reads result.attachments[] | | Cypress | Native — reads run.screenshots[] by testId | | WebdriverIO | Via attachScreenshot() in afterTest hook | | Puppeteer | Pass screenshotPath to endTest() | | Selenium | Same as Puppeteer | | Jest / Vitest | Via attachScreenshot() in afterEach |

The collector inlines small images (≤200 KB by default) as base64 data URIs so the report stays a single portable file. Larger images are copied to <outputDir>/screenshots/. Click any thumbnail for a full-screen lightbox (ESC to close).

Config:

screenshots: {
  enabled:            true,   // default
  onFailure:          true,   // default
  onPass:             false,  // default
  inlineThresholdKb:  200,    // default
  outputSubdir:       'screenshots',
}

🧩 Failure Clustering

Failures with similar normalized error signatures collapse into a single card with a ×N badge. Under the hood:

  • Timestamps, UUIDs, hex, absolute paths, and integers are normalized out of the message + top stack frame
  • Failed tests are bucketed by signature
  • When AI is enabled, analysis runs once per cluster (representative test), not once per test — massive token savings

Example: three timeout tests with different test names but the same error string produce one ×3 cluster with a single AI-generated root cause + fix.


🤖 AI Failure Analysis (optional)

We never call any AI service by default. Bring your own key.

ai: {
  enabled: true,
  provider: 'anthropic',                        // or 'openai' | 'gemini'
  apiKey:   process.env.ANTHROPIC_API_KEY,
  model:    'claude-3-5-haiku-20241022',        // provider default used if omitted
  maxFailuresToAnalyze: 10,
}

Per cluster you get:

  • Summary — plain English
  • Root cause — what actually caused it
  • Suggestion — concrete fix
  • Confidence — high / medium / low

Getting a key:


🔔 Slack / Teams / Discord Webhooks

All three platforms get rich context, not just pass/fail counts:

  • Emoji + title with your reportTitle
  • Framework, pass rate, passed/failed/skipped, duration
  • Branch, commit hash, commit message (auto-detected)
  • +N new · −N recovered · N still failing vs previous run
  • Top 3 failure clusters with ×N counts
  • Top N failed test titles (configurable)
  • Clickable buttons: 📊 View report, 🔀 PR #123, ⚙️ CI job
  • Optional @mention on regressions
webhooks: {
  slack:   process.env.SLACK_WEBHOOK_URL,
  teams:   process.env.TEAMS_WEBHOOK_URL,
  discord: process.env.DISCORD_WEBHOOK_URL,

  reportUrl:          'https://reports.example.com/latest',
  notifyOnFailOnly:   true,
  maxFailedInCard:    5,
  mentionOnRegression: 'U0123ABC',   // Slack user or group id
}

Setup, per platform:

  • Slack — App Directory → Incoming Webhooks → add to a channel → copy URL
  • Teams — Channel → Connectors → Incoming Webhook → configure → copy URL
  • Discord — Channel settings → Integrations → Webhooks → New → copy URL

📊 Cross-Run Insights

Enable history and the report gains a pass-rate sparkline, a trend line chart, failure-state badges (🆕 / 💥 / 🔁), and a diff banner comparing to the previous run.

history: {
  enabled:     true,
  maxRuns:     20,                          // default
  historyFile: '.qapulse-history.json',     // auto-created inside outputDir
}

Each stored run includes per-test outcomes, so QAPulseSK-report can tell whether a currently-failing test is:

  • 🆕 new — never seen before
  • 💥 regression — passed last time, fails now
  • 🔁 recurring (×N) — failed for the last N runs in a row

🏷 Auto Git & CI Metadata

Every report auto-detects and displays:

  • Git: branch, short commit, commit message, author, tag
  • CI: provider, job URL, PR number + link

Detected providers: GitHub Actions, GitLab CI, CircleCI, Jenkins, Bitbucket Pipelines, and generic CI=true fallback.

Disable entirely with disableAutoMetadata: true, or override:

// Attach freely — user metadata always wins over auto-detected
run.metadata = { git: { branch: 'my-override' }, custom: { anything: 'ok' } };

📦 JSON Export

Alongside qapulse-report.html you get qapulse-report.json with the full normalized TestRun, clusters, diff, and failure states. Great for:

  • Consumption by other tools (SAT, dashboards, CI gates)
  • Post-processing in Node/Python
  • Long-term storage / analytics

Disable with emitJson: false.


⚙️ Full Config Reference

{
  outputDir?:             string;     // default: 'qapulse-report'
  reportTitle?:           string;     // default: 'QAPulseSK Test Report'
  openAfterGeneration?:   boolean;    // default: false
  logo?:                  string;     // optional logo image path or URL

  theme?: {
    name?:               'qapulse-dark' | 'qapulse-light' | 'github-dark' |
                         'github-light' | 'dracula' | 'solarized-light' | 'minimal';
    primaryColor?:       string;
    backgroundColor?:    string;
    cardColor?:          string;
  };

  ai?: {
    enabled:               boolean;
    provider?:             'anthropic' | 'openai' | 'gemini';
    apiKey?:               string;
    model?:                string;
    maxFailuresToAnalyze?: number;  // default: 10
  };

  webhooks?: {
    slack?:                string;
    teams?:                string;
    discord?:              string;
    custom?:               Array<{ url: string; headers?: Record<string,string>; template?: (run) => object }>;
    notifyOnFailOnly?:     boolean;
    reportUrl?:            string;
    mentionOnRegression?:  string;
    mentionOnNewFailures?: boolean;
    maxFailedInCard?:      number;  // default: 5
  };

  history?: {
    enabled:      boolean;
    historyFile?: string;
    maxRuns?:     number;  // default: 20
  };

  screenshots?: {
    enabled?:            boolean;  // default: true
    onFailure?:          boolean;  // default: true
    onPass?:             boolean;  // default: false
    inlineThresholdKb?:  number;   // default: 200
    outputSubdir?:       string;   // default: 'screenshots'
  };

  emitJson?:             boolean;  // default: true
  disableAutoMetadata?:  boolean;  // default: false
}

🧪 See It In Action

The with-packages branch of the Playwright boilerplate wires qapulsesk-report and ships a dedicated demo spec:

git clone -b with-packages https://github.com/QAPulse-by-SK/playwright-boilerplate.git
cd playwright-boilerplate && npm install && npx playwright install

npx playwright test tests/packages/report.demo.spec.ts --project=chromium
open qapulse-report/qapulse-report.html

Live demo report: qapulse-report-sk.surge.sh


📋 Changelog

v2.3.0

  • Rich Slack / Teams / Discord webhooks: failed test list, cluster summary, diff vs previous run, git/PR link, "View report" button, optional @-mention on regressions
  • reportUrl config for clickable button in cards
  • Live-fire webhook smoke test (npm run smoke:webhooks)

v2.2.0

  • 🧩 Failure clustering — local error-signature bucketing; AI runs once per cluster instead of per test
  • 🔍 Insights section — suite health matrix, duration histogram, top 10 slowest, per-suite execution timeline (all zero-dep SVG)
  • Diff vs previous run+N new / −N recovered / N still failing banner
  • Failure-state badges — 🆕 new / 💥 regression / 🔁 recurring (×N) per failing test
  • Auto git/CI metadata bar — branch, commit, author, PR + job links; supports GitHub Actions / GitLab / CircleCI / Jenkins / Bitbucket
  • JSON exportqapulse-report.json alongside HTML
  • Sparkline in pass-rate card
  • Fix: history save now ensures output dir exists

v2.1.0

  • 7 built-in themes — qapulse-dark, qapulse-light, github-dark, github-light, dracula, solarized-light, minimal
  • Legacy color overrides still work

v2.0.0

  • Screenshots on failure across all runners (inline base64 ≤200 KB, else copied) with click-to-zoom lightbox
  • 3 new runners: Puppeteer, Selenium, WebdriverIO — 4 → 7
  • New attachScreenshot(fullTestName, path) registry for runner-agnostic capture
  • Extracted shared orchestrator from adapter pipelines

v1.0.x

  • Initial releases: Playwright, Cypress, Jest, Vitest adapters
  • Dark-theme HTML report
  • Slack / Teams webhooks
  • AI failure analysis
  • Trend history

🔗 Related Packages

| Package | Description | |---------|-------------| | qapulsesk-assert | Fuzzy assertions, schema validation, AI-powered checks | | qapulsesk-gen | HAR → tests, recordings → tests, plain English → tests | | qapulsesk-healer | Self-healing locators for Playwright/Selenium |


MIT © QA Pulse by SK

Created by QA Pulse by SK · skakarh.com


🌐 More from QA Pulse by SK

| | | |---|---| | 🌐 Website | www.skakarh.com | | 📦 All Open Source Products | skakarh.com/products | | ✍️ QA Automation Blog | skakarh.com/blog | | 🛠️ QA Consulting Services | skakarh.com/services | | 🏢 GitHub Organisation | github.com/QAPulse-by-SK | | 🎭 Playwright Boilerplate | github.com/QAPulse-by-SK/playwright-boilerplate | | 🌲 Cypress Boilerplate | github.com/QAPulse-by-SK/cypress-boilerplate | | 🐍 Selenium Boilerplate | github.com/QAPulse-by-SK/selenium-boilerplate | | 📦 qapulsesk-assert | npmjs.com/package/qapulsesk-assert | | 🤖 qapulsesk-gen | npmjs.com/package/qapulsesk-gen |