CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-japa--runner

A simple yet powerful testing framework for Node.js backend applications and libraries

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

plugins.mddocs/

Plugin System

Plugin architecture for extending functionality with built-in and custom plugins.

Capabilities

Plugin Function Interface

Core plugin function signature for creating custom plugins.

/**
 * Plugin function receives an instance of the runner, emitter, config and CLI args
 * @param japa - Plugin context with runner, emitter, config, and CLI arguments
 */
interface PluginFn {
  (japa: {
    config: NormalizedConfig;
    cliArgs: CLIArgs;
    runner: Runner;
    emitter: Emitter;
  }): void | Promise<void>;
}

Disallow Pinned Tests Plugin

Built-in plugin that prevents pinned tests from running in production or CI environments.

/**
 * Disallows pinned tests by throwing an error before the runner starts executing tests
 * @param options - Plugin configuration options
 * @returns Plugin function
 */
function disallowPinnedTests(options?: {
  /** Whether to disallow pinned tests (default: true) */
  disallow?: boolean;
  /** Custom error message to display */
  errorMessage?: string;
}): PluginFn;

Usage Examples:

import { configure } from "@japa/runner";
import { disallowPinnedTests } from "@japa/runner/plugins";

// Basic usage - disallow pinned tests
configure({
  files: ["tests/**/*.spec.ts"],
  plugins: [
    disallowPinnedTests(),
  ],
});

// Custom configuration
configure({
  files: ["tests/**/*.spec.ts"],
  plugins: [
    disallowPinnedTests({
      disallow: process.env.NODE_ENV === "production",
      errorMessage: "Pinned tests are not allowed in production environment",
    }),
  ],
});

// Conditional based on environment
const isProduction = process.env.NODE_ENV === "production";
configure({
  files: ["tests/**/*.spec.ts"],
  plugins: [
    disallowPinnedTests({
      disallow: isProduction,
      errorMessage: isProduction 
        ? "Production builds cannot contain pinned tests"
        : "Pinned tests found - remove .pin() before deployment",
    }),
  ],
});

Custom Plugin Creation

Create custom plugins to extend test runner functionality.

Usage Examples:

import { configure } from "@japa/runner";

// Test timing plugin
const testTimingPlugin = () => {
  const testTimes = new Map();
  
  return ({ runner, emitter }) => {
    emitter.on("test:start", (payload) => {
      testTimes.set(payload.title, Date.now());
    });
    
    emitter.on("test:end", (payload) => {
      const startTime = testTimes.get(payload.title);
      if (startTime) {
        const duration = Date.now() - startTime;
        if (duration > 1000) {
          console.warn(`Slow test detected: ${payload.title} (${duration}ms)`);
        }
        testTimes.delete(payload.title);
      }
    });
  };
};

// Database cleanup plugin
const dbCleanupPlugin = () => {
  return ({ runner, emitter, config }) => {
    emitter.on("runner:start", async () => {
      console.log("Setting up test database...");
      await setupTestDatabase();
    });
    
    emitter.on("runner:end", async () => {
      console.log("Cleaning up test database...");
      await cleanupTestDatabase();
    });
    
    emitter.on("test:end", async (payload) => {
      if (payload.hasError) {
        await rollbackTestTransaction();
      }
    });
  };
};

// Environment validation plugin
const envValidationPlugin = (requiredEnvVars: string[]) => {
  return ({ config, cliArgs }) => {
    for (const envVar of requiredEnvVars) {
      if (!process.env[envVar]) {
        throw new Error(`Required environment variable ${envVar} is not set`);
      }
    }
    
    console.log("✓ All required environment variables are present");
  };
};

// Coverage threshold plugin
const coverageThresholdPlugin = (threshold: number) => {
  return ({ runner, emitter }) => {
    emitter.on("runner:end", () => {
      const summary = runner.getSummary();
      const passRate = (summary.aggregates.passed / summary.aggregates.total) * 100;
      
      if (passRate < threshold) {
        console.error(`Test coverage ${passRate.toFixed(1)}% is below threshold ${threshold}%`);
        process.exitCode = 1;
      }
    });
  };
};

// Use custom plugins
configure({
  files: ["tests/**/*.spec.ts"],
  plugins: [
    testTimingPlugin(),
    dbCleanupPlugin(),
    envValidationPlugin(["DATABASE_URL", "API_KEY"]),
    coverageThresholdPlugin(80),
  ],
});

Plugin Event System

Plugins can listen to various events throughout the test execution lifecycle.

// Available events for plugin integration
interface PluginEvents {
  "runner:start": () => void;
  "runner:end": () => void;
  "suite:start": (payload: { name: string }) => void;
  "suite:end": (payload: { name: string; hasError: boolean }) => void;
  "group:start": (payload: { title: string }) => void;
  "group:end": (payload: { title: string; hasError: boolean }) => void;
  "test:start": (payload: { title: string }) => void;
  "test:end": (payload: { 
    title: string; 
    hasError: boolean; 
    duration: number;
    errors: Error[];
  }) => void;
}

Usage Examples:

// Comprehensive event logging plugin
const eventLoggerPlugin = () => {
  return ({ emitter }) => {
    emitter.on("runner:start", () => {
      console.log("🏃 Test runner started");
    });
    
    emitter.on("suite:start", (payload) => {
      console.log(`📂 Suite started: ${payload.name}`);
    });
    
    emitter.on("group:start", (payload) => {
      console.log(`📁 Group started: ${payload.title}`);
    });
    
    emitter.on("test:start", (payload) => {
      console.log(`🧪 Test started: ${payload.title}`);
    });
    
    emitter.on("test:end", (payload) => {
      const status = payload.hasError ? "❌" : "✅";
      console.log(`${status} Test completed: ${payload.title} (${payload.duration}ms)`);
    });
    
    emitter.on("group:end", (payload) => {
      const status = payload.hasError ? "❌" : "✅";
      console.log(`${status} Group completed: ${payload.title}`);
    });
    
    emitter.on("suite:end", (payload) => {
      const status = payload.hasError ? "❌" : "✅";
      console.log(`${status} Suite completed: ${payload.name}`);
    });
    
    emitter.on("runner:end", () => {
      console.log("🏁 Test runner finished");
    });
  };
};

Plugin Configuration Integration

Plugins can access and modify configuration during initialization.

Usage Examples:

// Dynamic suite configuration plugin
const dynamicSuitePlugin = () => {
  return ({ config, runner }) => {
    // Add dynamic suite configuration
    runner.onSuite((suite) => {
      if (suite.name === "integration") {
        suite.timeout(30000);
      }
    });
    
    // Modify global configuration based on environment
    if (process.env.NODE_ENV === "test") {
      config.timeout = Math.max(config.timeout, 5000);
    }
  };
};

// Conditional plugin loading
const conditionalFeaturesPlugin = () => {
  return ({ config, cliArgs, emitter }) => {
    // Enable additional features based on CLI args
    if (cliArgs.verbose) {
      emitter.on("test:start", (payload) => {
        console.log(`Starting test: ${payload.title}`);
      });
    }
    
    // Modify configuration based on detected environment
    if (process.env.CI) {
      config.forceExit = true;
    }
  };
};

Types

Plugin Context Types

interface PluginContext {
  config: NormalizedConfig;
  cliArgs: CLIArgs;
  runner: Runner;
  emitter: Emitter;
}

interface NormalizedConfig {
  cwd: string;
  timeout: number;
  retries: number;
  filters: Filters;
  configureSuite: (suite: Suite) => void;
  reporters: {
    activated: string[];
    list: NamedReporterContract[];
  };
  plugins: PluginFn[];
  importer: (filePath: URL) => void | Promise<void>;
  refiner: Refiner;
  forceExit: boolean;
  setup: SetupHookHandler[];
  teardown: TeardownHookHandler[];
  exclude: string[];
}

interface CLIArgs {
  _?: string[];
  tags?: string | string[];
  files?: string | string[];
  tests?: string | string[];
  groups?: string | string[];
  timeout?: string;
  retries?: string;
  reporters?: string | string[];
  forceExit?: boolean;
  failed?: boolean;
  help?: boolean;
  matchAll?: boolean;
  listPinned?: boolean;
  bail?: boolean;
  bailLayer?: string;
}

Event Payload Types

interface TestEndPayload {
  title: string;
  hasError: boolean;
  duration: number;
  errors: Error[];
}

interface SuiteEndPayload {
  name: string;
  hasError: boolean;
}

interface GroupEndPayload {
  title: string;
  hasError: boolean;
}

docs

configuration.md

core-api.md

core-classes.md

factories.md

index.md

plugins.md

reporters.md

test-management.md

tile.json