Wocha Docs

Testing Library

Mock providers, test tokens, webhook payloads, and Playwright/Cypress helpers for Wocha auth.

@wocha/testing helps you test Wocha integrations without hitting the hosted login flow. Mock React auth state in component tests, inject encrypted session cookies for Next.js E2E tests, and generate signed webhook payloads for handler testing.

Installation

Add as a dev dependency:

pnpm add -D @wocha/testing
Or scaffold with the CLI
npx @wocha/cli init --framework nextjs

Optional peers:

  • @wocha/react — required for MockWochaProvider
  • @wocha/nextjs — session cookies match its encryption format
  • Playwright or Cypress — use framework-specific subpath imports

Mock providers

MockWochaProvider is a drop-in replacement for WochaProvider from @wocha/react. It wires the same context so hooks like useSession, useOrg, and usePermission work without OAuth.

import { render, screen } from "@testing-library/react";
import { useSession } from "@wocha/react";
import { MockWochaProvider, createMockUser } from "@wocha/testing";
 
function Profile() {
  const { data, status } = useSession();
  if (status !== "authenticated") return null;
  return <p>{data?.email}</p>;
}
 
test("renders authenticated user", () => {
  render(
    <MockWochaProvider
      user={createMockUser({ email: "test@example.com" })}
      org={{ orgId: "org_123" }}
    >
      <Profile />
    </MockWochaProvider>,
  );
 
  expect(screen.getByText("test@example.com")).toBeInTheDocument();
});

Props: session, user, org, isLoading, isAuthenticated, error, config — all optional partial overrides.

Mock data factories

import {
  createMockUser,
  createMockOrg,
  createMockSession,
  createTestSession,
} from "@wocha/testing";
 
const user = createMockUser({ email: "a@example.com", orgId: "org_1" });
const org = createMockOrg({ slug: "acme", displayName: "Acme Inc" });
const session = createMockSession({ user: { email: "a@example.com" } });
const testSession = createTestSession({ email: "a@example.com", orgId: "org_1" });

Factories accept partial overrides and generate realistic IDs by default. Use createTestSession() when you need a full StoredSession shape for cookie encryption tests.

Test tokens

Generate fake JWTs and encrypted session cookies for integration tests without a live OAuth flow.

Low-level API

import {
  createTestingSessionCookie,
  generateTestingAccessToken,
} from "@wocha/testing";
 
// Encrypted cookie matching @wocha/nextjs format
const { name, value, accessToken, session } = await createTestingSessionCookie({
  email: "dev@example.com",
  userId: "user_abc",
  orgId: "org_xyz",
  roles: ["member"],
  permissions: ["billing:view"],
  sessionSecret: process.env.WOCHA_CLIENT_SECRET,
});
 
// JWT only (API integration tests)
const token = generateTestingAccessToken({
  email: "dev@example.com",
  orgId: "org_xyz",
});

Important: Pass the same secret your app uses (WOCHA_CLIENT_SECRET or WOCHA_COOKIE_SECRET / sessionSecret in createWochaHandler).

Environment variables

VariablePurpose
WOCHA_TEST_TOKEN_SECRETHS256 secret for test access tokens (default: built-in test secret)
WOCHA_COOKIE_SECRET / WOCHA_CLIENT_SECRETSession cookie encryption (must match your app)
WOCHA_WEBHOOK_SECRETWebhook signing secret for payload generator

Webhook payloads

Generate signed webhook payloads compatible with @wocha/sdk/webhooks verification:

import { createTestWebhookPayload } from "@wocha/testing";
 
const { payload, headers } = createTestWebhookPayload("user.created", {
  secret: "whsec_test",
  data: { email: "new-user@example.com" },
});
 
const response = await fetch("http://localhost:3000/api/webhooks/wocha", {
  method: "POST",
  headers,
  body: payload,
});

Supported event types include user.created, user.updated, session.created, session.revoked, organisation.created, organisation.member_added, connection.created, application.created, and more. Import WEBHOOK_EVENT_TYPES for the full list of types supported by the test generator:

import { WEBHOOK_EVENT_TYPES, createTestWebhookPayload } from "@wocha/testing";
 
console.log(WEBHOOK_EVENT_TYPES);

Verify in tests:

import {
  createTestWebhookPayload,
  verifyTestWebhookSignature,
} from "@wocha/testing";
 
const { payload, headers } = createTestWebhookPayload("session.revoked");
expect(
  verifyTestWebhookSignature(payload, headers["X-Wocha-Signature"], "whsec_test"),
).toBe(true);

Or use the production SDK verifier:

import { constructWebhookEvent } from "@wocha/sdk/webhooks";
 
const event = await constructWebhookEvent(
  payload,
  headers["X-Wocha-Signature"],
  "whsec_test",
);

Playwright integration

@wocha/nextjs stores sessions in an encrypted greet_session cookie. The Playwright helper sets a valid cookie so middleware and server components see an authenticated user.

import { test, expect } from "@playwright/test";
import { setupGreetTestingToken } from "@wocha/testing/playwright";
 
test("authenticated dashboard", async ({ page }) => {
  await setupGreetTestingToken(page, {
    email: "test@example.com",
    orgId: "org_123",
    roles: ["admin"],
    permissions: ["documents:read"],
    sessionSecret: process.env.WOCHA_CLIENT_SECRET,
  });
 
  await page.goto("/dashboard");
  await expect(page.getByText("Welcome")).toBeVisible();
});

Pass a BrowserContext instead of a Page to share auth state across tests:

test.beforeEach(async ({ context }) => {
  await setupGreetTestingToken(context, { email: "test@example.com" });
});

Cypress integration

Register the custom command in cypress/support/e2e.ts:

import { registerGreetTestingCommands } from "@wocha/testing/cypress";
 
registerGreetTestingCommands();

Use in specs:

describe("dashboard", () => {
  beforeEach(() => {
    cy.greetSignIn({
      email: "test@example.com",
      orgId: "org_123",
      sessionSecret: Cypress.env("WOCHA_CLIENT_SECRET"),
    });
  });
 
  it("loads for signed-in users", () => {
    cy.visit("/dashboard");
    cy.contains("Welcome").should("be.visible");
  });
});

Testing patterns

Unit test (React component)

import { render, screen } from "@testing-library/react";
import { Protect } from "@wocha/ui";
import { MockWochaProvider, createMockUser } from "@wocha/testing";
 
test("shows admin panel for admins", () => {
  render(
    <MockWochaProvider user={createMockUser({ roles: ["admin"] })}>
      <Protect role="admin">
        <div>Admin panel</div>
      </Protect>
    </MockWochaProvider>,
  );
 
  expect(screen.getByText("Admin panel")).toBeInTheDocument();
});

Integration test (webhook handler)

import { describe, it, expect } from "vitest";
import { createTestWebhookPayload } from "@wocha/testing";
import { POST } from "@/app/api/webhooks/wocha/route";
 
describe("webhook handler", () => {
  it("processes user.created", async () => {
    const { payload, headers } = createTestWebhookPayload("user.created", {
      secret: process.env.WOCHA_WEBHOOK_SECRET,
    });
 
    const request = new Request("http://localhost/api/webhooks/wocha", {
      method: "POST",
      headers,
      body: payload,
    });
 
    const response = await POST(request);
    expect(response.status).toBe(200);
  });
});

E2E test (Next.js + Playwright)

import { test, expect } from "@playwright/test";
import { setupGreetTestingToken } from "@wocha/testing/playwright";
 
test.describe("protected routes", () => {
  test.beforeEach(async ({ page }) => {
    await setupGreetTestingToken(page, {
      email: "e2e@example.com",
      sessionSecret: process.env.WOCHA_CLIENT_SECRET!,
    });
  });
 
  test("dashboard loads without login redirect", async ({ page }) => {
    await page.goto("/dashboard");
    await expect(page).not.toHaveURL(/login/);
  });
});

Package exports

ImportUse case
@wocha/testingMock provider, factories, tokens, webhooks, WEBHOOK_EVENT_TYPES
@wocha/testing/playwrightsetupGreetTestingToken(page | context, options)
@wocha/testing/cypressregisterGreetTestingCommands(), cy.greetSignIn()

Security note

Testing tokens and default secrets are for local and CI test environments only. Never enable test token generation in production. Use dedicated test tenants and rotate secrets if they are ever committed to source control.

Learn more