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

saas-testing-toolkit

v1.0.1

Published

Enterprise SaaS testing infrastructure — Vitest + Playwright + pgTAP + SOC2

Downloads

186

Readme

SaaS Testing Toolkit

Drop-in test infrastructure for Next.js + Supabase apps. Scans your codebase, generates RBAC tests, RLS verification, and a full E2E suite — then runs them against your actual schema.


What you get

  • RBAC matrix — one test per role × route, auto-built from your middleware.ts
  • RLS verification — pgTAP tests that prove tenant data never crosses org boundaries
  • E2E suite — smoke, CRUD, features, lifecycle, and accessibility specs, all seeded and torn down automatically
  • Compliance exports — SOC2 audit evidence in JSON, ready for an auditor
  • Unit + component tests — generated from your server actions and React components, assertions automatically filled from return-type inference
  • Security specs — session management, error disclosure, rate limiting, SRI checks
  • npm run check — pre-flight checker that tells you exactly what's missing before you run anything

Everything is generated from your actual schema and routes. You don't maintain a parallel test config that drifts away from production.


Requirements

| | Minimum | |---|---| | Node.js | 20.0.0 | | npm | 10.0.0 | | Next.js | App Router (src/app/) | | Database | Supabase (PostgreSQL + RLS) | | Supabase CLI | Latest (brew install supabase/tap/supabase) |

Pages Router is not supported. If your project uses pages/, migrate to App Router first.


Install

1. Copy the toolkit into your project

cp -r saas-testing-toolkit/ path/to/your-project/
cd path/to/your-project
npm install

2. Set up environment variables

cp .env.test.example .env.test.local

Then start your local Supabase instance and fill in the values it prints:

supabase start
# Prints: API URL, anon key, service role key — paste into .env.test.local

Fill in E2E_ADMIN_EMAIL, E2E_ADMIN_PASSWORD, and BASE_URL (your local app URL, e.g. http://localhost:3001).

3. Run the pre-flight check

npm run check

This verifies everything is in place before you run a single test. You'll see something like:

  ✓  Node.js version  v22.1.0 (≥20 required)
  ✓  .env.test.local  found
  ✓  Required env vars  all 6 set
  ✓  Seed strategy  A — supabase/migrations/
  ✓  Middleware  middleware.ts (5 protected routes in matcher)
  ✓  Database schema  supabase/prod_schema.sql (14 tables)
  ✓  Playwright browsers  chromium installed
  ⚠  Codegen output  e2e/pages/ is empty — codegen hasn't been run yet
     → Run: npm run codegen

  No blockers.  Run: npm run codegen

Fix any errors before continuing. warnings are fine to proceed with.

4. Export your schema (if not already done)

supabase db dump --local > supabase/prod_schema.sql

Skip this if supabase/prod_schema.sql already exists.

5. Run codegen

npm run codegen -- --yes

This runs a 7-step pipeline that scans your project and generates:

Documentation

  • docs/TESTING-PROJECT.md — your project's testing spec: detected roles, tables, routes, modules
  • docs/TESTING-MASTER.md — generic testing strategy template for team onboarding

Test infrastructure

  • src/__tests__/factories/index.ts — factory function per database table
  • src/__tests__/{actions,components,unit}/*.test.ts — tests for your server actions and components, with assertions filled automatically from return-type inference
  • src/__tests__/msw-handlers.ts — MSW mock handlers for your API routes

E2E infrastructure

  • e2e/pages/*.ts — Playwright page objects with your real URL paths and locators
  • e2e/config/toolkit.config.tsPRIMARY_ENTITY, all page routes, API routes, audit actions, and org-scoped tables in correct teardown order — no TODO comments, no manual editing
  • e2e/security/rbac.config.ts — one entry per protected route, with inferred allowedRoles
  • e2e/compliance/audit-log.spec.ts — audit trail coverage
  • e2e/compliance/data-deletion.spec.ts — right-to-erasure coverage

Database

  • supabase/tests/001–004.sql — pgTAP tests for RLS, org isolation, role boundaries
  • scripts/schema.config.ts — schema contract with all required tables and critical columns — used by npm run verify-migration to catch deploy-time drift

Run npm run codegen -- --dry-run first to preview what will be generated without writing any files.

6. Run the smoke suite

npm run test:local:smoke

Smoke should go green on first run. If it doesn't, the error output will tell you exactly which selector or URL needs updating in e2e/auth.setup.ts.


How it works

YOUR CODEBASE                     TOOLKIT
─────────────────                 ───────────────────────────────────────
supabase/prod_schema.sql    ─┐
src/app/**/page.tsx          │   scan  →  generate  →  you fill in
middleware.ts                │
src/lib/actions/*.ts         │
src/components/**/*.tsx     ─┘

Scan detects:
  • Tables, columns, enums, RLS policies
  • Routes and their page types (list / detail / create / edit)
  • Which routes are protected and what roles are required
  • Server action function signatures
  • React component form fields

Generate produces:
  • RBAC matrix wired to your actual routes and roles
  • Test files with correct imports, mocks, and real expect() assertions
  • Page objects with your real URL paths and locators
  • E2E config with primary entity, routes, audit actions, tables (no TODOs)
  • pgTAP tests with your real table names
  • Schema contract with every table and critical column

You fill in:
  • CRUD and feature spec bodies in e2e/crud/ and e2e/features/
  • Any login form selectors that differ from standard input[type="email/password"]
  • E2E spec assertions that require running the app (smoke, a11y, lifecycle)

Reviewing the generated output

Most generated files require no manual editing. These two are worth a quick look:

e2e/auth.setup.ts Codegen fills the post-login redirect URL from your routes. Verify the login form selectors match your actual markup if your login page uses non-standard inputs:

await page.goto("/login");                           // ← auto-detected from routes
await page.fill('input[type="email"]', email);       // ← update if your selector differs
await page.fill('input[type="password"]', password); // ← update if your selector differs
await page.click('button[type="submit"]');
await page.waitForURL("/dashboard");                  // ← auto-detected from routes

src/__tests__/setup.ts Verify the vi.mock("@/lib/supabase/server") path matches your actual Supabase client location. If your project keeps its clients in a non-standard path, update the two vi.mock(...) calls.

Everything else — e2e/config/toolkit.config.ts, scripts/schema.config.ts, src/__tests__/factories/, and all test files — is fully generated with no TODO comments.


Reference demo

The demo/ directory contains a minimal Contact CRM SaaS (Next.js 15 + Supabase) built specifically to show the pipeline in action against a realistic codebase. It uses CHECK constraints for roles (not PostgreSQL ENUMs), the org_role JWT claim (not role), and a 5-table schema — exactly the pattern most real Supabase projects follow.

Running npm run codegen -- --yes against the demo produces all 7 steps passing and 93 files generated with zero manual configuration.

See demo/README.md for the recorded proof-of-value run output.


Command reference

Setup and generation

| Command | What it does | |---|---| | npm run check | Pre-flight check — verify all prerequisites before running tests | | npm run codegen | Run all generators against the current project | | npm run setup | Interactive first-time setup wizard (human-only, not for CI) |

Unit tests

| Command | What it does | |---|---| | npm test | Vitest unit tests (watch mode) | | npm run test:coverage | Vitest with V8 coverage report | | npm run test:mutation | Stryker mutation testing — proves tests actually catch bugs |

E2E tests

| Command | What it does | |---|---| | npm run test:local | Full Playwright suite (all projects) | | npm run test:local:smoke | Smoke only — fastest gate, run on every PR | | npm run test:local:security | RBAC matrix + session + error disclosure | | npm run test:local:a11y | Accessibility scans (axe-core) | | npm run test:local:compliance | Audit log + data deletion specs |

Database tests and reporting

| Command | What it does | |---|---| | supabase test db | Run pgTAP tests (RLS, org isolation, role boundaries, auth required) | | npm run report:compliance | Export SOC2 evidence JSON | | npm run verify-migration | Post-deploy schema integrity check |


The E2E suite structure

Tests run in dependency order, sharing a single seeded database:

db-setup  (global.setup.ts — auto-detects and runs your seed strategy)
  └── setup  (auth.setup.ts — logs in once, stores session cookie)
        ├── smoke/       happy path only, no mutations — runs on every PR
        ├── crud/        create → list → edit → delete per primary entity
        ├── features/    search, filters, pagination, import, file upload
        ├── lifecycle/   cross-entity flows (e.g. submission → placement)
        └── a11y/        axe-core scans on load and after interaction

security/    RBAC matrix, session hijacking, error disclosure (independent)
compliance/  Audit log integrity, data deletion (runs after crud/)

Seed strategy is auto-detected — no manual config needed:

| What exists in your project | What runs | |---|---| | supabase/migrations/ or supabase/seed.sql | supabase db reset --local | | scripts/seed-test-db.ts | npx tsx scripts/seed-test-db.ts | | src/app/api/test/seed/route.ts + SEED_SECRET | POST /api/test/seed |

If none of these exist, the suite fails immediately with instructions for which file to create.


CI/CD

Three workflows are included in .github/workflows/:

ci.yml — triggers on every PR Smoke suite only. Typically finishes in under 3 minutes. Blocks merge on failure.

staging.yml — triggers on push to main Full suite against the staging deployment: smoke → CRUD → security → compliance.

nightly.yml — runs at 2 AM Security suite, accessibility scans, mutation testing, and SSL certificate expiry check.

To connect them to your project, set BASE_URL and PLAYWRIGHT_BASE_URL as GitHub Actions repository variables pointing at your environments.


Security and compliance coverage

| Spec | What it tests | |---|---| | rbac-matrix.spec.ts | Every protected route returns 401/403 for unauthorised roles | | session-management.spec.ts | Session expiry, token rotation, logout invalidation | | error-disclosure.spec.ts | Stack traces and internal paths never appear in HTTP responses | | rate-limiting.spec.ts | Auth endpoints enforce rate limits | | sri.spec.ts | External scripts have integrity attributes | | audit-log.spec.ts | Create/update/delete actions write audit entries | | data-deletion.spec.ts | DELETE removes all PII from every related table | | supabase/tests/001–004.sql | RLS enabled, org isolation, role boundaries, auth required |

npm run report:compliance exports all passing results to a JSON file suitable for attaching to a SOC2 Type II audit.


Compatibility

| Stack | Support | |---|---| | Next.js App Router | Full | | Next.js Pages Router | Not supported — migrate to App Router first | | Supabase Auth | Full — roles auto-detected from schema enums | | Clerk | Full — roles auto-detected | | NextAuth / Auth.js v5 | Full — roles auto-detected | | Other auth providers | Roles default to ["admin","editor","viewer"]; set ALL_ROLES manually in rbac.config.ts | | Prisma / Drizzle | Vitest + Playwright layers work; pgTAP and seed scripts require Supabase CLI |


Troubleshooting

npm run check reports no seed strategy Create one of the detection targets. For any Supabase project, the fastest fix is creating supabase/seed.sql (even if empty) — it signals Strategy A.

Smoke suite fails with "waiting for URL /dashboard" Your post-login redirect differs from the default. In e2e/auth.setup.ts, change waitForURL("/dashboard") to your actual redirect path.

rbac.config.ts ROUTES array is empty after codegen Codegen reads config.matcher from middleware.ts. Create it with a matcher listing your protected routes, then re-run npm run codegen.

pgTAP tests fail with "relation does not exist" The generated SQL uses table names from your schema at the time codegen ran. If you renamed a table, re-run python3 docs/generate-pgtap-tests.py --project-root ..

supabase db reset --local fails during db-setup Make sure supabase start is running before executing any test command that touches the database.