import '@testing-library/jest-dom';
import type { NFTError } from '@/api/types';
import { fireEvent, render } from '@testing-library/react';
import type { TransactionReceipt } from 'viem';
import { describe, expect, it, vi } from 'vitest';
import { type LifecycleStatus, Lifecycle, MediaType } from '../types';
import {
NFTLifecycleProvider,
useNFTLifecycleContext,
} from './NFTLifecycleProvider';
const TestComponent = () => {
const context = useNFTLifecycleContext();
const handleStatusError = async () => {
context.updateLifecycleStatus({
statusName: 'error',
statusData: {
code: 'code',
error: 'error_long_messages',
message: '',
},
});
};
const handleStatusSuccessWithTransaction = async () => {
context.updateLifecycleStatus({
statusName: 'success',
statusData: {
transactionReceipts: ['0x123'] as unknown as TransactionReceipt[],
},
});
};
const handleStatusSuccessWithoutTransaction = async () => {
context.updateLifecycleStatus({
statusName: 'success',
statusData: {},
});
};
const handleStatusMediaLoading = async () => {
context.updateLifecycleStatus({
statusName: 'mediaLoading',
statusData: {
mediaType: MediaType.Image,
mediaUrl: 'https://example.com/image.png',
},
});
};
return (
{context.lifecycleStatus.statusName}
{context.lifecycleStatus.statusName === 'error' && (
{context.lifecycleStatus.statusData.code}
)}
);
};
const renderWithProviders = ({
Component,
onError = vi.fn(),
onStatus = vi.fn(),
onSuccess = vi.fn(),
}: {
Component: () => React.ReactNode;
onError?: (error: NFTError) => void;
onStatus?: (lifecycleStatus: LifecycleStatus) => void;
onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
}) => {
return render(
,
);
};
describe('NFTLifecycleProvider', () => {
it('should throw an error if useNFTLifecycleContext is used outside of NFTLifecycleProvider', () => {
const TestComponent = () => {
useNFTLifecycleContext();
return null;
};
// Suppress console.error for this test to avoid noisy output
const originalError = console.error;
console.error = vi.fn();
expect(() => {
render();
}).toThrow(
'useNFTLifecycleContext must be used within an NFTView or NFTMint component',
);
// Restore console.error
console.error = originalError;
});
it('should call onSuccess callback on status success with transaction', async () => {
const onSuccessMock = vi.fn();
const { getByText } = renderWithProviders({
Component: TestComponent,
onSuccess: onSuccessMock,
});
const button = getByText('setLifecycleStatus.successWithTransaction');
fireEvent.click(button);
expect(onSuccessMock).toHaveBeenCalledWith('0x123');
});
it('should call onSuccess callback on status success without transaction', async () => {
const onSuccessMock = vi.fn();
const { getByText } = renderWithProviders({
Component: TestComponent,
onSuccess: onSuccessMock,
});
const button = getByText('setLifecycleStatus.successWithoutTransaction');
fireEvent.click(button);
expect(onSuccessMock).toHaveBeenCalledWith(undefined);
});
it('should call onError callback on status error', async () => {
const onErrorMock = vi.fn();
const { getByText } = renderWithProviders({
Component: TestComponent,
onError: onErrorMock,
});
const button = getByText('setLifecycleStatus.error');
fireEvent.click(button);
expect(onErrorMock).toHaveBeenCalled();
});
it('should call onStatus callback on any status change', async () => {
const onStatusMock = vi.fn();
const { getByText } = renderWithProviders({
Component: TestComponent,
onStatus: onStatusMock,
});
const button = getByText('setLifecycleStatus.mediaLoading');
fireEvent.click(button);
expect(onStatusMock).toHaveBeenCalled();
});
});