Skip to content

Commit 36c3f76

Browse files
HazATclaude
andcommitted
feat(lighthouse-ci): add SENTRY_LIGHTHOUSE_MODE guard to default-browser, nextjs-16, react-router-7-spa
Instrument three E2E test applications to conditionally initialize Sentry based on the SENTRY_LIGHTHOUSE_MODE build-time environment variable. This enables Lighthouse CI to measure performance across three feature levels: - no-sentry: dynamic import is skipped entirely, allowing treeshaking to remove all SDK code for a clean baseline measurement - init-only: Sentry.init() runs with no additional integrations (measures SDK core overhead) - tracing-replay: full integrations enabled (measures feature overhead) - unset/empty: preserves existing E2E behavior (all features enabled) Each app uses the env var prefix appropriate for its bundler: - default-browser (webpack): process.env.SENTRY_LIGHTHOUSE_MODE - nextjs-16 (Next.js): process.env.NEXT_PUBLIC_SENTRY_LIGHTHOUSE_MODE - react-router-7-spa (Vite): import.meta.env.PUBLIC_SENTRY_LIGHTHOUSE_MODE The async IIFE + dynamic import pattern is used (instead of top-level await) because webpack production builds may not support TLA. This also ensures clean treeshaking in no-sentry mode since the import itself is conditional. The default-browser build.mjs EnvironmentPlugin is changed from array form to object form with empty-string defaults so the build succeeds when SENTRY_LIGHTHOUSE_MODE is not set (existing E2E jobs don't set it). Bundle-output treeshake verification deferred to PR CI (the Lighthouse workflow job builds apps with the env var set and runs Lighthouse against the output). Ref: TODO-5805679c Co-Authored-By: Claude claude-opus-4-6 <noreply@anthropic.com>
1 parent d496226 commit 36c3f76

4 files changed

Lines changed: 106 additions & 75 deletions

File tree

dev-packages/e2e-tests/test-applications/default-browser/build.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ webpack(
1818
minimizer: [new TerserPlugin()],
1919
},
2020
plugins: [
21-
new webpack.EnvironmentPlugin(['E2E_TEST_DSN']),
21+
new webpack.EnvironmentPlugin({ E2E_TEST_DSN: '', SENTRY_LIGHTHOUSE_MODE: '' }),
2222
new HtmlWebpackPlugin({
2323
template: path.join(__dirname, 'public/index.html'),
2424
}),
Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
1-
import * as Sentry from '@sentry/browser';
1+
const lighthouseMode = process.env.SENTRY_LIGHTHOUSE_MODE;
22

3-
Sentry.init({
4-
dsn: process.env.E2E_TEST_DSN,
5-
integrations: [Sentry.browserTracingIntegration()],
6-
tracesSampleRate: 1.0,
7-
release: 'e2e-test',
8-
environment: 'qa',
9-
tunnel: 'http://localhost:3031',
10-
});
3+
(async () => {
4+
if (lighthouseMode !== 'no-sentry') {
5+
const Sentry = await import('@sentry/browser');
116

12-
document.getElementById('exception-button').addEventListener('click', () => {
13-
throw new Error('I am an error!');
14-
});
7+
const integrations = [];
158

16-
document.getElementById('navigation-link').addEventListener('click', () => {
17-
document.getElementById('navigation-target').scrollIntoView({ behavior: 'smooth' });
18-
});
9+
if (lighthouseMode !== 'init-only') {
10+
integrations.push(Sentry.browserTracingIntegration());
11+
}
12+
13+
Sentry.init({
14+
dsn: process.env.E2E_TEST_DSN,
15+
integrations,
16+
tracesSampleRate: 1.0,
17+
release: 'e2e-test',
18+
environment: 'qa',
19+
tunnel: 'http://localhost:3031',
20+
});
21+
}
22+
23+
document.getElementById('exception-button').addEventListener('click', () => {
24+
throw new Error('I am an error!');
25+
});
26+
27+
document.getElementById('navigation-link').addEventListener('click', () => {
28+
document.getElementById('navigation-target').scrollIntoView({ behavior: 'smooth' });
29+
});
30+
})();
Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
import * as Sentry from '@sentry/nextjs';
22
import type { Log } from '@sentry/nextjs';
33

4-
Sentry.init({
5-
environment: 'qa', // dynamic sampling bias to keep transactions
6-
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
7-
tunnel: `http://localhost:3031/`, // proxy server
8-
tracesSampleRate: 1.0,
9-
sendDefaultPii: true,
10-
integrations: [
11-
Sentry.thirdPartyErrorFilterIntegration({
12-
filterKeys: ['nextjs-16-e2e'],
13-
behaviour: 'apply-tag-if-contains-third-party-frames',
14-
}),
15-
],
16-
// Verify Log type is available
17-
beforeSendLog(log: Log) {
18-
return log;
19-
},
20-
});
4+
const lighthouseMode = process.env.NEXT_PUBLIC_SENTRY_LIGHTHOUSE_MODE;
215

22-
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
6+
if (lighthouseMode !== 'no-sentry') {
7+
const integrations: Sentry.Integration[] = [];
8+
9+
if (lighthouseMode !== 'init-only') {
10+
integrations.push(
11+
Sentry.thirdPartyErrorFilterIntegration({
12+
filterKeys: ['nextjs-16-e2e'],
13+
behaviour: 'apply-tag-if-contains-third-party-frames',
14+
}),
15+
);
16+
}
17+
18+
Sentry.init({
19+
environment: 'qa', // dynamic sampling bias to keep transactions
20+
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
21+
tunnel: `http://localhost:3031/`, // proxy server
22+
tracesSampleRate: 1.0,
23+
sendDefaultPii: true,
24+
integrations,
25+
// Verify Log type is available
26+
beforeSendLog(log: Log) {
27+
return log;
28+
},
29+
});
30+
}
31+
32+
export const onRouterTransitionStart = lighthouseMode !== 'no-sentry' ? Sentry.captureRouterTransitionStart : undefined;
Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as Sentry from '@sentry/react';
21
import React from 'react';
32
import ReactDOM from 'react-dom/client';
43
import {
@@ -14,43 +13,53 @@ import Index from './pages/Index';
1413
import SSE from './pages/SSE';
1514
import User from './pages/User';
1615

17-
const replay = Sentry.replayIntegration();
18-
19-
Sentry.init({
20-
environment: 'qa', // dynamic sampling bias to keep transactions
21-
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
22-
integrations: [
23-
Sentry.reactRouterV7BrowserTracingIntegration({
24-
useEffect: React.useEffect,
25-
useLocation,
26-
useNavigationType,
27-
createRoutesFromChildren,
28-
matchRoutes,
29-
trackFetchStreamPerformance: true,
30-
}),
31-
replay,
32-
],
33-
// We recommend adjusting this value in production, or using tracesSampler
34-
// for finer control
35-
tracesSampleRate: 1.0,
36-
release: 'e2e-test',
37-
38-
// Always capture replays, so we can test this properly
39-
replaysSessionSampleRate: 1.0,
40-
replaysOnErrorSampleRate: 0.0,
41-
tunnel: 'http://localhost:3031',
42-
sendDefaultPii: true,
43-
});
44-
45-
const SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes);
46-
47-
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
48-
root.render(
49-
<BrowserRouter>
50-
<SentryRoutes>
51-
<Route path="/" element={<Index />} />
52-
<Route path="/user/:id" element={<User />} />
53-
<Route path="/sse" element={<SSE />} />
54-
</SentryRoutes>
55-
</BrowserRouter>,
56-
);
16+
const lighthouseMode = import.meta.env.PUBLIC_SENTRY_LIGHTHOUSE_MODE;
17+
18+
let SentryRoutes = Routes;
19+
20+
(async () => {
21+
if (lighthouseMode !== 'no-sentry') {
22+
const Sentry = await import('@sentry/react');
23+
24+
const integrations: Sentry.Integration[] = [];
25+
26+
if (lighthouseMode !== 'init-only') {
27+
integrations.push(
28+
Sentry.reactRouterV7BrowserTracingIntegration({
29+
useEffect: React.useEffect,
30+
useLocation,
31+
useNavigationType,
32+
createRoutesFromChildren,
33+
matchRoutes,
34+
trackFetchStreamPerformance: true,
35+
}),
36+
Sentry.replayIntegration(),
37+
);
38+
}
39+
40+
Sentry.init({
41+
environment: 'qa', // dynamic sampling bias to keep transactions
42+
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
43+
integrations,
44+
tracesSampleRate: 1.0,
45+
release: 'e2e-test',
46+
replaysSessionSampleRate: lighthouseMode !== 'init-only' ? 1.0 : 0.0,
47+
replaysOnErrorSampleRate: 0.0,
48+
tunnel: 'http://localhost:3031',
49+
sendDefaultPii: true,
50+
});
51+
52+
SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes);
53+
}
54+
55+
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
56+
root.render(
57+
<BrowserRouter>
58+
<SentryRoutes>
59+
<Route path="/" element={<Index />} />
60+
<Route path="/user/:id" element={<User />} />
61+
<Route path="/sse" element={<SSE />} />
62+
</SentryRoutes>
63+
</BrowserRouter>,
64+
);
65+
})();

0 commit comments

Comments
 (0)