import { render, screen, fireEvent, act } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import * as Sentry from '@sentry/react'; import { SentryTestButton, handleSentryTestErrorClick } from '../SentryTestButton'; vi.mock('@sentry/react', () => ({ captureException: vi.fn(), logger: { info: vi.fn() }, metrics: { count: vi.fn() }, })); /** * De echte fout wordt bewust ongevangen via een `setTimeout` gegooid (zie * SentryTestButton.tsx). Om dat in deze fake-timer-tests te simuleren zonder * dat de test zelf crasht op die intentionele throw, wordt het "doortikken" * van de timers hierin afgevangen. */ function advanceTimersIgnoringIntentionalThrow(ms: number) { try { act(() => { vi.advanceTimersByTime(ms); }); } catch { // verwacht: de intentionele test-fout uit handleSentryTestErrorClick } } describe('SentryTestButton', () => { beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('is visible in local development', () => { render(); expect(screen.getByRole('button', { name: 'Break the world' })).toBeInTheDocument(); }); it('sends a log and a metric to Sentry, then throws an intentional test error', () => { expect(() => handleSentryTestErrorClick()).toThrow('This is your first error!'); expect(Sentry.logger.info).toHaveBeenCalledWith('User triggered test error', { action: 'test_error_button_click', }); expect(Sentry.metrics.count).toHaveBeenCalledWith('test_counter', 1); }); it('shows a confirmation toast immediately when clicked, before the test error fires', () => { render(); fireEvent.click(screen.getByRole('button', { name: 'Break the world' })); expect(screen.getByRole('status')).toHaveTextContent('Test error verzonden naar Sentry'); // De log/metric/throw zijn pas gepland (setTimeout), nog niet uitgevoerd. expect(Sentry.logger.info).not.toHaveBeenCalled(); advanceTimersIgnoringIntentionalThrow(0); expect(Sentry.logger.info).toHaveBeenCalledWith('User triggered test error', { action: 'test_error_button_click', }); expect(Sentry.metrics.count).toHaveBeenCalledWith('test_counter', 1); }); it('hides the toast again after it has been shown for a while', () => { render(); fireEvent.click(screen.getByRole('button', { name: 'Break the world' })); expect(screen.getByRole('status')).toBeInTheDocument(); // De intentionele fout (0ms-timer) stopt het "doortikken" van timers // binnen dezelfde advanceTimersByTime-call, dus eerst die apart afhandelen // voordat de resterende hide-timer (4000ms) doorgetikt kan worden. advanceTimersIgnoringIntentionalThrow(0); advanceTimersIgnoringIntentionalThrow(4000); expect(screen.queryByRole('status')).not.toBeInTheDocument(); }); });