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

test-management.mddocs/

Test Management

Advanced test creation, grouping, and macro functionality for organizing complex test suites.

Capabilities

Test Groups

Create organized groups of related tests with shared setup, teardown, and configuration.

/**
 * Create a Japa test group
 * @param title - Group title
 * @param callback - Function that receives the group instance for configuration
 * @returns Group instance
 */
test.group(title: string, callback: (group: Group) => void): Group;

interface Group {
  add(test: Test): void;
  bail(toggle?: boolean): this;
  timeout(duration: number): this;
  retry(count: number): this;
  setup(handler: GroupHooksHandler<TestContext>): this;
  teardown(handler: GroupHooksHandler<TestContext>): this;
  each: {
    setup(handler: GroupHooksHandler<TestContext>): Group;
    teardown(handler: GroupHooksHandler<TestContext>): Group;
  };
  tap(handler: (test: Test) => void): this;
}

type GroupHooksHandler<Context> = (context: Context) => void | Promise<void>;

Usage Examples:

import { test } from "@japa/runner";
import { assert } from "chai";

test.group("User Authentication", (group) => {
  let authService: AuthService;
  
  // Setup before all tests in group
  group.setup(async () => {
    authService = new AuthService();
    await authService.initialize();
  });
  
  // Teardown after all tests in group
  group.teardown(async () => {
    await authService.cleanup();
  });
  
  // Setup before each test in group
  group.each.setup(async ({ context }) => {
    context.user = await createTestUser();
  });
  
  // Teardown after each test in group
  group.each.teardown(async ({ context }) => {
    await deleteTestUser(context.user.id);
  });
  
  test("should login with valid credentials", async (ctx) => {
    const result = await authService.login(ctx.context.user.email, "password");
    assert.isTrue(result.success);
  });
  
  test("should reject invalid credentials", async (ctx) => {
    const result = await authService.login("invalid@email.com", "wrong");
    assert.isFalse(result.success);
  });
});

// Group with timeout and retry configuration
test.group("API Integration Tests", (group) => {
  group.timeout(10000);
  group.retry(2);
  
  test("should fetch user data", async (ctx) => {
    // Test logic here
  });
});

Test Macros

Create reusable test bound macros that can access the currently executed test.

/**
 * Create a test bound macro. Within the macro, you can access the 
 * currently executed test to read its context values or define cleanup hooks.
 * @param callback - Macro function that receives the test instance as first parameter
 * @returns Function that can be called within tests
 */
test.macro<T extends (test: Test, ...args: any[]) => any>(
  callback: T
): (...args: OmitFirstArg<Parameters<T>>) => ReturnType<T>;

type OmitFirstArg<F> = F extends [_: any, ...args: infer R] ? R : never;

Usage Examples:

import { test } from "@japa/runner";
import { assert } from "chai";

// Create a macro for database operations
const withDatabase = test.macro((test: Test, tableName: string) => {
  test.setup(async () => {
    await createTable(tableName);
  });
  
  test.cleanup(async () => {
    await dropTable(tableName);
  });
  
  return {
    async insert(data: any) {
      return await db.table(tableName).insert(data);
    },
    async find(id: number) {
      return await db.table(tableName).find(id);
    },
  };
});

// Create a macro for API testing
const withApiClient = test.macro((test: Test, baseUrl: string) => {
  let client: ApiClient;
  
  test.setup(() => {
    client = new ApiClient(baseUrl);
  });
  
  test.cleanup(async () => {
    await client.close();
  });
  
  return client;
});

// Use macros in tests
test("should create and retrieve user", async (ctx) => {
  const db = withDatabase("users");
  const api = withApiClient("http://localhost:3000");
  
  const user = await db.insert({ name: "John", email: "john@example.com" });
  const retrieved = await db.find(user.id);
  
  assert.equal(retrieved.name, "John");
});

Active Test Management

Functions to access and manage the currently executing test.

/**
 * Get the currently running test instance
 * @returns Current test instance or undefined if not in test context
 */
function getActiveTest(): Test<any> | undefined;

/**
 * Get the currently running test instance or throw an error
 * @returns Current test instance
 * @throws Error if not in test context
 */
function getActiveTestOrFail(): Test<any>;

Usage Examples:

import { test, getActiveTest, getActiveTestOrFail } from "@japa/runner";
import { assert } from "chai";

// Helper function that works with current test
function addTestMetadata(key: string, value: any) {
  const currentTest = getActiveTest();
  if (currentTest) {
    currentTest.options.meta[key] = value;
  }
}

// Macro that uses active test
const trackTestExecution = test.macro(() => {
  const currentTest = getActiveTestOrFail();
  const startTime = Date.now();
  
  currentTest.cleanup(() => {
    const duration = Date.now() - startTime;
    console.log(`Test "${currentTest.title}" took ${duration}ms`);
  });
});

test("performance test", async (ctx) => {
  trackTestExecution();
  addTestMetadata("category", "performance");
  
  // Test logic here
  await someAsyncOperation();
  assert.isTrue(true);
});

Types

Group Types

interface Group {
  add(test: Test): void;
  bail(toggle?: boolean): this;
  timeout(duration: number): this;
  retry(count: number): this;
  setup(handler: GroupHooksHandler<TestContext>): this;
  teardown(handler: GroupHooksHandler<TestContext>): this;
  each: {
    setup(handler: GroupHooksHandler<TestContext>): Group;
    teardown(handler: GroupHooksHandler<TestContext>): Group;
  };
  tap(handler: (test: Test) => void): this;
}

type GroupHooksHandler<Context> = (context: Context) => void | Promise<void>;

Macro Types

type OmitFirstArg<F> = F extends [_: any, ...args: infer R] ? R : never;

docs

configuration.md

core-api.md

core-classes.md

factories.md

index.md

plugins.md

reporters.md

test-management.md

tile.json