Add temporary Sentry test button (dev/test only)
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { Nav } from './Nav';
|
||||
import { Footer } from './Footer';
|
||||
import { SentryTestButton } from './SentryTestButton';
|
||||
|
||||
export function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
@@ -8,6 +9,7 @@ export function RootLayout({ children }: { children: ReactNode }) {
|
||||
<Nav />
|
||||
{children}
|
||||
<Footer />
|
||||
<SentryTestButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
|
||||
/**
|
||||
* Alleen zichtbaar tijdens lokale development (`pnpm dev`) en in de testomgeving
|
||||
* (waar de build met `VITE_APP_ENV=test` wordt gebouwd, zie continuous_integration.yaml).
|
||||
* In een productiebuild (VITE_APP_ENV ontbreekt of is 'production') is deze knop
|
||||
* altijd verborgen, ook als iemand vergeet de variabele expliciet te zetten.
|
||||
*/
|
||||
const isVisible = import.meta.env.DEV || import.meta.env.VITE_APP_ENV === 'test';
|
||||
|
||||
/**
|
||||
* Losstaand van het component zodat dit direct (zonder DOM/React event-dispatch)
|
||||
* getest kan worden. React vangt fouten uit event handlers namelijk NIET op via
|
||||
* een ErrorBoundary (die vangt alleen render-/lifecycle-fouten). De fout hieronder
|
||||
* blijft dus bewust een onafgevangen (uncaught) fout, die door Sentry's eigen
|
||||
* globale `window.onerror`-handler wordt opgepikt — precies zoals bedoeld voor
|
||||
* deze test-knop.
|
||||
*/
|
||||
export function handleSentryTestErrorClick(): void {
|
||||
// Send a log before throwing the error
|
||||
Sentry.logger.info('User triggered test error', {
|
||||
action: 'test_error_button_click',
|
||||
});
|
||||
// Send a test metric before throwing the error
|
||||
Sentry.metrics.count('test_counter', 1);
|
||||
throw new Error('This is your first error!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tijdelijke testknop om te verifiëren dat Sentry errors, logs en metrics
|
||||
* daadwerkelijk binnenkomen.
|
||||
*/
|
||||
export function SentryTestButton() {
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSentryTestErrorClick}
|
||||
className="fixed bottom-4 right-4 z-50 rounded bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-lg hover:bg-red-700"
|
||||
>
|
||||
Break the world
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, beforeEach } 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() },
|
||||
}));
|
||||
|
||||
describe('SentryTestButton', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -15,6 +15,8 @@ if (sentryDsn) {
|
||||
integrations: [Sentry.tanstackRouterBrowserTracingIntegration(router)],
|
||||
// Low-traffic marketing site: capture all traces (lower this if traffic grows significantly).
|
||||
tracesSampleRate: 1.0,
|
||||
// Required for Sentry.logger.* calls (e.g. the temporary SentryTestButton) to actually be sent.
|
||||
enableLogs: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
@@ -3,6 +3,8 @@
|
||||
interface ImportMetaEnv {
|
||||
/** Sentry DSN (Data Source Name) for client-side error reporting. Not a secret — safe to expose in the client bundle. */
|
||||
readonly VITE_SENTRY_DSN?: string;
|
||||
/** Build-time deployment environment tag ('test' | 'production' | undefined). Used to hide dev/test-only UI (e.g. SentryTestButton) from production builds. */
|
||||
readonly VITE_APP_ENV?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
Reference in New Issue
Block a user