Skip to main content

Testing Your Integration

This guide covers testing strategies for your Fanfare integration, including unit testing with mocks, integration testing with the staging environment, and end-to-end testing.

Test Utilities

The Fanfare SDK provides test utilities for mocking the SDK in your tests.

Core SDK Mock

Import the mock SDK factory from the test utilities:
import { createMockFanfareSDK } from "@fanfare/sdk/test-utils";

const mockSDK = createMockFanfareSDK();
The mock SDK provides default implementations for all methods:
const mockSDK = createMockFanfareSDK({
  // Override specific methods
  queues: {
    enter: async () => ({
      position: 5,
      estimatedWaitTimeInSeconds: 120,
      status: "QUEUED",
    }),
  },
});

React Testing

For React components, use the MockFanfareProvider:
import { render, screen } from "@testing-library/react";
import { MockFanfareProvider } from "@fanfare/react/mocks";
import { createMockSDK } from "@fanfare/react/mocks";
import { QueueWidget } from "@fanfare/react";

describe("QueueWidget", () => {
  it("shows queue position after entering", async () => {
    const mockSDK = createMockSDK({
      queues: {
        get: async () => ({
          id: "queue_123",
          name: "Test Queue",
          status: "open",
        }),
        status: async () => ({
          status: "QUEUED",
          position: 5,
        }),
      },
    });

    render(
      <MockFanfareProvider sdk={mockSDK}>
        <QueueWidget experienceId="exp_123" />
      </MockFanfareProvider>
    );

    // Assert on rendered output
    expect(await screen.findByText(/position/i)).toBeInTheDocument();
  });
});

Testing Hooks

Test hooks using @testing-library/react:
import { renderHook, waitFor } from "@testing-library/react";
import { MockFanfareProvider } from "@fanfare/react/mocks";
import { createMockSDK } from "@fanfare/react/mocks";
import { useQueue } from "@fanfare/react";

describe("useQueue", () => {
  it("enters queue and returns position", async () => {
    const mockSDK = createMockSDK({
      queues: {
        enter: async () => ({
          position: 1,
          estimatedWaitTimeInSeconds: 60,
          status: "QUEUED",
        }),
      },
    });

    const wrapper = ({ children }: { children: React.ReactNode }) => (
      <MockFanfareProvider sdk={mockSDK}>{children}</MockFanfareProvider>
    );

    const { result } = renderHook(() => useQueue("queue_123"), { wrapper });

    // Enter the queue
    await result.current.enter();

    await waitFor(() => {
      expect(result.current.position).toBe(1);
    });
  });
});

Mock Data Fixtures

The SDK provides pre-built mock data for common entities:
import {
  mockQueue,
  mockQueueEnterResult,
  mockQueueStatus,
  mockDraw,
  mockDrawStatus,
  mockAuction,
  mockAuctionStatus,
  mockWaitlistEntry,
  mockTimedRelease,
  mockExperience,
} from "@fanfare/sdk/test-utils";

// Use in your tests
const mockSDK = createMockFanfareSDK({
  queues: {
    get: async () => mockQueue,
    enter: async () => mockQueueEnterResult,
    status: async () => mockQueueStatus,
  },
});

Available Mock Data

MockDescription
mockQueueBasic queue object
mockQueueEnterResultResult of entering a queue
mockQueueStatusConsumer’s queue status
mockDrawBasic draw object
mockDrawStatusConsumer’s draw status
mockAuctionBasic auction object
mockAuctionStatusConsumer’s auction status
mockWaitlistEntryWaitlist entry
mockTimedReleaseTimed release object
mockExperienceBasic experience object

Testing Event Handlers

Mock SDK events using the on method override:
import { createMockFanfareSDK } from "@fanfare/sdk/test-utils";

describe("Queue admission handling", () => {
  it("handles admission event", async () => {
    const listeners: Record<string, ((data: unknown) => void)[]> = {};

    const mockSDK = createMockFanfareSDK({
      on: (event: string, handler: (data: unknown) => void) => {
        if (!listeners[event]) {
          listeners[event] = [];
        }
        listeners[event].push(handler);
        return () => {
          listeners[event] = listeners[event].filter((h) => h !== handler);
        };
      },
    });

    // Set up your component with the mock SDK...

    // Trigger an event
    listeners["queue:admitted"]?.forEach((handler) => {
      handler({
        queueId: "queue_123",
        token: "admission_token_xyz",
      });
    });

    // Assert that your component handled the event
  });
});

Testing with Staging Environment

For integration tests, use the staging environment with test credentials:
import { FanfareProvider } from "@fanfare/react";

function TestApp() {
  return (
    <FanfareProvider
      organizationId={process.env.FANFARE_TEST_ORG_ID!}
      publishableKey={process.env.FANFARE_TEST_PUBLISHABLE_KEY!}
      environment="staging"
    >
      <YourApp />
    </FanfareProvider>
  );
}

Staging Environment Setup

  1. Create a test organization in your Fanfare dashboard
  2. Generate test API keys (prefixed with pk_test_)
  3. Create test experiences for different scenarios
  4. Use the staging API endpoint: https://api-staging.fanfare.io

Local Development Server

For local development with the full Fanfare stack:
<FanfareProvider
  organizationId="org_dev123"
  publishableKey="pk_dev_xyz789"
  environment="development"
>
This connects to http://localhost:4802 (the consumer-app service).

Running the Local Stack

If you have access to the Fanfare development environment:
# In the waitify-mono repository
pnpm install
pnpm infra:up        # Start PostgreSQL, Valkey, LocalStack
pnpm db:migrate      # Run database migrations
pnpm dev:apps        # Start all services
Services run on:
  • Admin API: http://localhost:4800
  • Admin Client: http://localhost:4801
  • Consumer API: http://localhost:4802
  • Beacon: http://localhost:4803

Testing Different Scenarios

Queue Full Scenario

const fullQueueSDK = createMockFanfareSDK({
  queues: {
    enter: async () => {
      throw new FanfareError("QUEUE_FULL", "Queue is at capacity");
    },
  },
});

Authentication Required

const authRequiredSDK = createMockFanfareSDK({
  experiences: {
    findSequence: async () => ({
      requiresAuthentication: true,
      requiresAccessCode: false,
    }),
  },
});

Draw Lost Scenario

const drawLostSDK = createMockFanfareSDK({
  draws: {
    checkResult: async () => ({
      won: false,
      drawId: "draw_123",
    }),
  },
});

Auction Outbid Scenario

const outbidSDK = createMockFanfareSDK({
  auctions: {
    placeBid: async () => ({
      status: "outbid",
      amount: "100.00",
      highestBid: "150.00",
      bidCount: 5,
    }),
  },
});

Network Error

const networkErrorSDK = createMockFanfareSDK({
  queues: {
    enter: async () => {
      throw new Error("Network request failed");
    },
  },
});

End-to-End Testing

For E2E tests with Playwright or Cypress, use the staging environment:

Playwright Example

// e2e/queue.spec.ts
import { test, expect } from "@playwright/test";

test("user can join queue and be admitted", async ({ page }) => {
  // Navigate to your app
  await page.goto("/product/limited-edition");

  // Click join queue button
  await page.click('button:has-text("Join Queue")');

  // Wait for queue position to appear
  await expect(page.locator(".queue-position")).toBeVisible();

  // Simulate time passing (use test controls in staging)
  // Or wait for actual admission in a long-running test

  // Verify admission
  await expect(page.locator('text="You are admitted"')).toBeVisible({
    timeout: 120000, // 2 minutes
  });
});

Cypress Example

// cypress/e2e/queue.cy.ts
describe("Queue Experience", () => {
  it("allows user to join queue", () => {
    cy.visit("/product/limited-edition");

    cy.contains("Join Queue").click();

    cy.get(".queue-position").should("be.visible");

    // Intercept and mock the admission for faster tests
    cy.intercept("GET", "**/queues/*/status", {
      statusCode: 200,
      body: {
        status: "ADMITTED",
        admissionToken: "test_token_123",
      },
    });

    cy.contains("You are admitted").should("be.visible");
  });
});

Test Data Management

Cleaning Up Test Data

After running tests, clean up test consumers and participations:
// In your test teardown
afterEach(async () => {
  // Leave any active queues
  await mockSDK.queues.leave("queue_123");

  // Clear local storage
  localStorage.clear();
  sessionStorage.clear();
});

Seeding Test Data

For integration tests, seed your test environment with data:
// test-setup.ts
import { init } from "@fanfare/sdk";

async function seedTestData() {
  const sdk = await init({
    organizationId: process.env.FANFARE_TEST_ORG_ID!,
    publishableKey: process.env.FANFARE_TEST_PUBLISHABLE_KEY!,
    environment: "staging",
  });

  // Create test session
  await sdk.auth.guest();

  // Enter experiences for testing
  await sdk.experiences.enter("exp_test_queue");

  return sdk;
}

Debugging Tests

Enable Debug Logging

<FanfareProvider
  organizationId="org_test123"
  publishableKey="pk_test_xyz789"
  debug={true}
  logging={{ level: "debug" }}
>

Inspect SDK State

const sdk = useFanfare();

// Log current state
console.log("Session:", sdk.auth.getSession());
console.log("Active experiences:", await sdk.getActiveExperiences());

Network Request Inspection

In your browser’s DevTools, filter network requests by:
  • api.fanfare.io (production)
  • api-staging.fanfare.io (staging)
  • localhost:4802 (local development)

CI/CD Integration

GitHub Actions Example

# .github/workflows/test.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm test
        env:
          FANFARE_TEST_ORG_ID: ${{ secrets.FANFARE_TEST_ORG_ID }}
          FANFARE_TEST_PUBLISHABLE_KEY: ${{ secrets.FANFARE_TEST_PUBLISHABLE_KEY }}

      - name: Run E2E tests
        run: npm run test:e2e
        env:
          FANFARE_TEST_ORG_ID: ${{ secrets.FANFARE_TEST_ORG_ID }}
          FANFARE_TEST_PUBLISHABLE_KEY: ${{ secrets.FANFARE_TEST_PUBLISHABLE_KEY }}

Best Practices

1. Isolate Tests

Each test should start with a clean state:
beforeEach(() => {
  localStorage.clear();
  sessionStorage.clear();
});

2. Mock External Dependencies

Always mock the SDK in unit tests to avoid network calls:
// Good
const mockSDK = createMockFanfareSDK();

// Avoid in unit tests
const realSDK = await init({ ... });

3. Test Error States

Always test error handling:
it("handles queue full error gracefully", async () => {
  const mockSDK = createMockFanfareSDK({
    queues: {
      enter: async () => {
        throw new Error("Queue is full");
      },
    },
  });

  // ... test that error is displayed to user
});

4. Use Realistic Timing

When testing time-based features (auctions, draws), use realistic but shortened timeframes:
const shortAuction = {
  ...mockAuction,
  closeAt: new Date(Date.now() + 5000).toISOString(), // 5 seconds
};

Next Steps