Herstructureer src/ naar feature-based folderstructuur
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / prepare (pull_request) Successful in 1m26s
Continuous Integration / build-production (pull_request) Skipped
Continuous Integration / build (pull_request) Successful in 2m6s
Continuous Integration / test (pull_request) Successful in 2m2s
Continuous Integration / deploy-production (pull_request) Skipped
Deploy / deploy (pull_request) Successful in 38s
Continuous Integration / deploy-test (pull_request) Successful in 39s
Continuous Integration / config (pull_request) Successful in 10s
Continuous Integration / prepare (pull_request) Successful in 1m26s
Continuous Integration / build-production (pull_request) Skipped
Continuous Integration / build (pull_request) Successful in 2m6s
Continuous Integration / test (pull_request) Successful in 2m2s
Continuous Integration / deploy-production (pull_request) Skipped
Deploy / deploy (pull_request) Successful in 38s
Continuous Integration / deploy-test (pull_request) Successful in 39s
Volgt de aanpak uit het React 2025 folder structure artikel en het
SlpModularCms/frontend referentieproject: components/{layout,ui,shared},
features/landing/{components,data,hooks,pages,__tests__}, en lib/ voor
de query client. routes/index.tsx wordt een dunne wrapper rond de nieuwe
features/landing/pages/LandingPage.tsx.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
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 the current (development/test) build', () => {
|
||||
render(<SentryTestButton />);
|
||||
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(<SentryTestButton />);
|
||||
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(<SentryTestButton />);
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user