import { render, screen } from '@testing-library/react'; import React, { useContext } from 'react'; import { createPannaClient, type PannaClient } from '../../core'; import { PannaProvider, PannaClientContext } from './panna-provider'; // Mock the thirdweb module jest.mock('thirdweb/react', () => ({ ThirdwebProvider: ({ children }: { children: React.ReactNode }) => (
{children}
) })); // Mock the createPannaClient function jest.mock('../../core', () => ({ createPannaClient: jest.fn() })); const mockCreatePannaClient = createPannaClient as jest.MockedFunction< typeof createPannaClient >; // Test component to consume the context const TestConsumer: React.FC = () => { const context = useContext(PannaClientContext); return (
{context?.client ? 'client-available' : 'client-null'} {JSON.stringify(context)}
); }; describe('PannaProvider', () => { beforeEach(() => { jest.clearAllMocks(); }); describe('rendering', () => { it('should render children without crashing', () => { render(
Test Child
); expect(screen.getByTestId('test-child')).toBeInTheDocument(); }); it('should wrap children with ThirdwebProvider', () => { render(
Test Child
); expect(screen.getByTestId('thirdweb-provider')).toBeInTheDocument(); expect(screen.getByTestId('test-child')).toBeInTheDocument(); }); }); describe('client creation and context', () => { it('should provide null client when no clientId is provided', () => { render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-null' ); expect(mockCreatePannaClient).not.toHaveBeenCalled(); }); it('should provide null client when clientId is undefined', () => { render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-null' ); expect(mockCreatePannaClient).not.toHaveBeenCalled(); }); it('should provide null client when clientId is empty string', () => { render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-null' ); expect(mockCreatePannaClient).not.toHaveBeenCalled(); }); it('should create and provide client when valid clientId is provided', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); render( ); expect(mockCreatePannaClient).toHaveBeenCalledWith({ clientId: 'test-client-id' }); expect(mockCreatePannaClient).toHaveBeenCalledTimes(1); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); }); }); describe('client memoization', () => { it('should not recreate client when clientId remains the same', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); const { rerender } = render( ); expect(mockCreatePannaClient).toHaveBeenCalledTimes(1); // Re-render with same clientId rerender( ); // Should not call createPannaClient again expect(mockCreatePannaClient).toHaveBeenCalledTimes(1); }); it('should recreate client when clientId changes', () => { const mockClient1 = { id: 'test-client-1' } as unknown as PannaClient; const mockClient2 = { id: 'test-client-2' } as unknown as PannaClient; mockCreatePannaClient .mockReturnValueOnce(mockClient1) .mockReturnValueOnce(mockClient2); const { rerender } = render( ); expect(mockCreatePannaClient).toHaveBeenCalledWith({ clientId: 'test-client-id-1' }); expect(mockCreatePannaClient).toHaveBeenCalledTimes(1); // Re-render with different clientId rerender( ); expect(mockCreatePannaClient).toHaveBeenCalledWith({ clientId: 'test-client-id-2' }); expect(mockCreatePannaClient).toHaveBeenCalledTimes(2); }); it('should change from client to null when clientId is removed', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); const { rerender } = render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); // Re-render without clientId rerender( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-null' ); }); it('should change from null to client when clientId is added', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); const { rerender } = render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-null' ); // Re-render with clientId rerender( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); expect(mockCreatePannaClient).toHaveBeenCalledWith({ clientId: 'test-client-id' }); }); }); describe('integration', () => { it('should work with multiple nested consumers', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); const NestedConsumer: React.FC = () => { const context = useContext(PannaClientContext); return (
{context?.client ? 'nested-has-client' : 'nested-no-client'}
); }; render(
); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); expect(screen.getByTestId('nested-consumer')).toHaveTextContent( 'nested-has-client' ); }); it('should maintain context across re-renders with dynamic children', () => { const mockClient = { id: 'test-client' } as unknown as PannaClient; mockCreatePannaClient.mockReturnValue(mockClient); const DynamicChildren: React.FC<{ showExtra: boolean }> = ({ showExtra }) => ( <> {showExtra &&
Extra Content
} ); const { rerender } = render( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); expect(screen.queryByTestId('extra-content')).not.toBeInTheDocument(); rerender( ); expect(screen.getByTestId('client-status')).toHaveTextContent( 'client-available' ); expect(screen.getByTestId('extra-content')).toBeInTheDocument(); }); }); });