Documentation Index Fetch the complete documentation index at: https://docs.fanfare.io/llms.txt
Use this file to discover all available pages before exploring further.
Testing Strategies
Thorough testing is essential before high-stakes product launches. This guide covers testing approaches from development through production readiness.
Testing Environments
Development Mode
Use test API keys during development.
const client = new FanfareClient ({
publishableKey: "pk_test_..." , // Test key
debug: true , // Enable debug logging
});
Test mode provides:
Isolated data from production
Faster rate limits for testing
Debug logging enabled
No real transactions
Staging Environment
Test with production-like data and configuration.
const client = new FanfareClient ({
publishableKey: process . env . FANFARE_PUBLISHABLE_KEY ,
apiUrl: process . env . FANFARE_API_URL , // Optional: staging endpoint
debug: process . env . NODE_ENV !== "production" ,
});
Unit Testing
Testing SDK Integration
Mock the Fanfare SDK for isolated component tests.
// __mocks__/@fanfare/sdk.ts
export class MockFanfareClient {
experiences = {
enter: jest . fn (). mockResolvedValue ({
experienceId: "exp_123" ,
enteredAt: new Date (). toISOString (),
}),
leave: jest . fn (). mockResolvedValue ( undefined ),
getActiveSession: jest . fn (). mockReturnValue ( null ),
};
queues = {
enter: jest . fn (). mockResolvedValue ({
queueId: "queue_123" ,
position: 50 ,
}),
getPosition: jest . fn (). mockReturnValue ( 50 ),
isInQueue: jest . fn (). mockReturnValue ( true ),
};
on = jest . fn ();
off = jest . fn ();
}
export const FanfareClient = MockFanfareClient ;
Component Test Example
import { render , screen , fireEvent , waitFor } from "@testing-library/react" ;
import { QueueWidget } from "./queue-widget" ;
jest . mock ( "@fanfare/sdk" );
describe ( "QueueWidget" , () => {
it ( "shows position after entering queue" , async () => {
render (< QueueWidget experienceId = "exp_123" />);
fireEvent . click ( screen . getByText ( "Join Queue" ));
await waitFor (() => {
expect ( screen . getByText ( /Position: 50/ )). toBeInTheDocument ();
});
});
it ( "handles queue full error gracefully" , async () => {
const { FanfareClient } = require ( "@fanfare/sdk" );
FanfareClient . prototype . queues . enter . mockRejectedValue (
new Error ( "QUEUE_FULL" )
);
render (< QueueWidget experienceId = "exp_123" />);
fireEvent . click ( screen . getByText ( "Join Queue" ));
await waitFor (() => {
expect ( screen . getByText ( /Queue is full/ )). toBeInTheDocument ();
});
});
});
Testing Event Handlers
describe ( "Queue event handling" , () => {
it ( "updates UI on position change" , () => {
const { FanfareClient } = require ( "@fanfare/sdk" );
let positionHandler : ( data : { position : number }) => void ;
FanfareClient . prototype . on . mockImplementation (( event , handler ) => {
if ( event === "queue:position-updated" ) {
positionHandler = handler ;
}
});
const { rerender } = render (< QueueWidget experienceId = "exp_123" />);
// Simulate position update event
positionHandler ({ position: 25 });
rerender (< QueueWidget experienceId = "exp_123" />);
expect ( screen . getByText ( /Position: 25/ )). toBeInTheDocument ();
});
});
Integration Testing
End-to-End Flow Tests
Test complete user journeys using test mode.
import { test , expect } from "@playwright/test" ;
test . describe ( "Queue Experience" , () => {
test ( "complete queue journey" , async ({ page }) => {
// Navigate to experience page
await page . goto ( "/experience/test-queue" );
// Enter the queue
await page . click ( '[data-testid="enter-queue"]' );
// Wait for position to display
await expect ( page . locator ( '[data-testid="queue-position"]' )). toBeVisible ();
// Verify position is a number
const position = await page . locator ( '[data-testid="queue-position"]' ). textContent ();
expect ( parseInt ( position || "0" )). toBeGreaterThan ( 0 );
// Wait for access (in test mode, this can be accelerated)
await expect ( page . locator ( '[data-testid="access-granted"]' )). toBeVisible ({ timeout: 60000 });
// Verify checkout redirect
await expect ( page ). toHaveURL ( / \/ checkout/ );
});
});
API Integration Tests
Test direct API interactions.
import { FanfareClient , FanfareError } from "@fanfare/sdk" ;
describe ( "API Integration" , () => {
let client : FanfareClient ;
beforeEach (() => {
client = new FanfareClient ({
publishableKey: process . env . FANFARE_TEST_KEY ! ,
});
});
afterEach ( async () => {
// Clean up any active sessions
try {
const session = client . experiences . getActiveSession ();
if ( session ) {
await client . experiences . leave ( session . experienceId );
}
} catch {
// Ignore cleanup errors
}
});
test ( "enter and leave experience" , async () => {
const session = await client . experiences . enter ( "exp_test_123" );
expect ( session . experienceId ). toBe ( "exp_test_123" );
expect ( session . enteredAt ). toBeDefined ();
await client . experiences . leave ( "exp_test_123" );
expect ( client . experiences . getActiveSession ()). toBeNull ();
});
test ( "handles invalid experience gracefully" , async () => {
await expect ( client . experiences . enter ( "exp_invalid" )). rejects . toThrow ( FanfareError );
});
});
Load Testing
Pre-Launch Load Tests
Validate your integration handles expected traffic.
// k6 load test script
import http from "k6/http" ;
import { check , sleep } from "k6" ;
import { Rate } from "k6/metrics" ;
const errorRate = new Rate ( "errors" );
export const options = {
stages: [
{ duration: "2m" , target: 100 }, // Ramp up
{ duration: "5m" , target: 500 }, // Normal load
{ duration: "2m" , target: 2000 }, // Peak load (launch moment)
{ duration: "5m" , target: 500 }, // Sustained
{ duration: "2m" , target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: [ "p(95)<1000" ], // 95% under 1s
errors: [ "rate<0.01" ], // Error rate under 1%
},
};
export default function () {
// Simulate page load
const pageRes = http . get ( "https://your-site.com/experience-page" );
check ( pageRes , {
"page loads" : ( r ) => r . status === 200 ,
});
// Simulate SDK initialization
const initRes = http . post ( "https://consumer.fanfare.io/experiences/exp_123/enter" , null , {
headers: {
"X-Publishable-Key" : "pk_test_..." ,
"Content-Type" : "application/json" ,
},
});
const success = check ( initRes , {
"experience entered" : ( r ) => r . status === 200 || r . status === 429 ,
});
errorRate . add ( ! success );
sleep ( Math . random () * 3 + 1 ); // Random 1-4s between requests
}
Metrics to Monitor
Metric Acceptable Warning Critical Response time (p95) < 500ms < 1000ms > 2000ms Error rate < 0.1% < 1% > 5% Throughput Stable Declining Crashing
Functional Testing Checklist
Experience Entry
User can enter an active experience
Entry is rejected for inactive experiences
Entry is rejected when at capacity
Re-entry returns existing session
Guest users can enter (if allowed) - Authenticated users can enter - Session persists across page refreshes -
Session survives temporary network issues
VIP users routed to priority sequence
Access codes grant correct sequence access
Users without access see appropriate messaging
Queue Behavior
Initial position is displayed
Position updates in real-time
Position never goes backward unexpectedly
Estimated wait time updates
Access notification is immediate - Handoff token is valid - Checkout redirect works - Access expires after timeout
User leaving and rejoining
Browser refresh during wait
Multiple tabs handling
Network disconnection and reconnection
Draw Behavior
Registration confirmation is shown
Entry number is assigned
Duplicate registration is prevented
Registration closes at scheduled time
Winners are notified promptly
Non-winners receive appropriate message
Winner checkout flow works
Waitlist option is available
Error Scenario Testing
Test how your integration handles errors.
describe ( "Error handling" , () => {
const errorScenarios = [
{
code: "NETWORK_ERROR" ,
userMessage: /connection issue/ i ,
shouldRetry: true ,
},
{
code: "RATE_LIMITED" ,
userMessage: /too many requests/ i ,
shouldRetry: true ,
},
{
code: "QUEUE_FULL" ,
userMessage: /queue is full/ i ,
shouldRetry: false ,
},
{
code: "SESSION_EXPIRED" ,
userMessage: /session expired/ i ,
shouldRetry: false ,
},
];
errorScenarios . forEach (({ code , userMessage , shouldRetry }) => {
test ( `handles ${ code } error` , async () => {
// Mock the error
mockClient . experiences . enter . mockRejectedValue (
new FanfareError ( "Error" , code )
);
render (< ExperienceWidget />);
fireEvent . click ( screen . getByText ( "Join" ));
// Check user-friendly message
await waitFor (() => {
expect ( screen . getByText ( userMessage )). toBeInTheDocument ();
});
// Check retry button presence
const retryButton = screen . queryByText ( /retry/ i );
expect ( !! retryButton ). toBe ( shouldRetry );
});
});
});
Pre-Launch Checklist
Before going live with a high-stakes launch:
Environment verification
Production API keys configured
Debug mode disabled
Error tracking enabled
Analytics configured
Functional validation
All user flows tested - Error scenarios handled - Mobile experience verified - Accessibility checked
Performance validation
Load tested at expected scale - Response times acceptable - No memory leaks - CDN caching configured
Monitoring setup
- Dashboards created - Alerts configured - On-call team ready - Runbooks prepared
Rollback plan
Rollback procedure documented
Previous version accessible
Database state reversible
Communication plan ready
Next Steps