Skip to content

Commit bdef6b0

Browse files
Luca ForstnerAbhiPrasad
authored andcommitted
Add e2e tests
1 parent a834f7a commit bdef6b0

File tree

4 files changed

+110
-34
lines changed

4 files changed

+110
-34
lines changed

packages/e2e-tests/test-applications/nextjs-app-dir/components/client-error-debug-tools.tsx

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { TransactionContext } from './transaction-context';
55
import { captureException } from '@sentry/nextjs';
66

77
export function ClientErrorDebugTools() {
8-
const { transactionActive, toggle } = useContext(TransactionContext);
8+
const transactionContextValue = useContext(TransactionContext);
9+
const [transactionName, setTransactionName] = useState<string>('');
910

1011
const [isFetchingAPIRoute, setIsFetchingAPIRoute] = useState<boolean>();
1112
const [isFetchingEdgeAPIRoute, setIsFetchingEdgeAPIRoute] = useState<boolean>();
@@ -18,13 +19,34 @@ export function ClientErrorDebugTools() {
1819

1920
return (
2021
<div>
21-
<button
22-
onClick={() => {
23-
toggle();
24-
}}
25-
>
26-
{transactionActive ? 'Stop Transaction' : 'Start Transaction'}
27-
</button>
22+
{transactionContextValue.transactionActive ? (
23+
<button
24+
onClick={() => {
25+
transactionContextValue.stop();
26+
setTransactionName('');
27+
}}
28+
>
29+
Stop transaction
30+
</button>
31+
) : (
32+
<>
33+
<input
34+
type="text"
35+
placeholder="Transaction name"
36+
value={transactionName}
37+
onChange={e => {
38+
setTransactionName(e.target.value);
39+
}}
40+
/>
41+
<button
42+
onClick={() => {
43+
transactionContextValue.start(transactionName);
44+
}}
45+
>
46+
Start transaction
47+
</button>
48+
</>
49+
)}
2850
<br />
2951
<br />
3052
<button

packages/e2e-tests/test-applications/nextjs-app-dir/components/transaction-context.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,36 @@ import { createContext, PropsWithChildren, useState } from 'react';
44
import { Transaction } from '@sentry/types';
55
import { startTransaction, getCurrentHub } from '@sentry/nextjs';
66

7-
export const TransactionContext = createContext<{ transactionActive: boolean; toggle: () => void }>({
7+
export const TransactionContext = createContext<
8+
{ transactionActive: false; start: (transactionName: string) => void } | { transactionActive: true; stop: () => void }
9+
>({
810
transactionActive: false,
9-
toggle: () => undefined,
11+
start: () => undefined,
1012
});
1113

1214
export function TransactionContextProvider({ children }: PropsWithChildren) {
1315
const [transaction, setTransaction] = useState<Transaction | undefined>(undefined);
1416

1517
return (
1618
<TransactionContext.Provider
17-
value={{
18-
transactionActive: !!transaction,
19-
toggle: () => {
20-
if (transaction) {
21-
transaction.finish();
22-
setTransaction(undefined);
23-
} else {
24-
const t = startTransaction({ name: 'Manual Transaction' });
25-
getCurrentHub().getScope()?.setSpan(t);
26-
setTransaction(t);
27-
}
28-
},
29-
}}
19+
value={
20+
transaction
21+
? {
22+
transactionActive: true,
23+
stop: () => {
24+
transaction.finish();
25+
setTransaction(undefined);
26+
},
27+
}
28+
: {
29+
transactionActive: false,
30+
start: (transactionName: string) => {
31+
const t = startTransaction({ name: transactionName });
32+
getCurrentHub().getScope()?.setSpan(t);
33+
setTransaction(t);
34+
},
35+
}
36+
}
3037
>
3138
{children}
3239
</TransactionContext.Provider>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { test } from '@playwright/test';
2+
import * as crypto from 'crypto';
3+
import { waitForTransaction } from '../../../test-utils/event-proxy-server';
4+
5+
if (process.env.TEST_ENV === 'production') {
6+
// TODO: Fix that this is flakey on dev server - might be an SDK bug
7+
test('Sends connected traces for server components', async ({ page }) => {
8+
await page.goto('/client-component');
9+
10+
const clientTransactionName = crypto.randomUUID();
11+
12+
const serverComponentTransaction = waitForTransaction('nextjs-13-app-dir', async transactionEvent => {
13+
return (
14+
transactionEvent?.transaction === 'Page Server Component (/server-component)' &&
15+
(await clientTransactionPromise).contexts?.trace?.trace_id === transactionEvent.contexts?.trace?.trace_id
16+
);
17+
});
18+
19+
const clientTransactionPromise = waitForTransaction('nextjs-13-app-dir', transactionEvent => {
20+
return transactionEvent?.transaction === clientTransactionName;
21+
});
22+
23+
await page.getByPlaceholder('Transaction name').fill(clientTransactionName);
24+
await page.getByText('Start transaction').click();
25+
await page.getByRole('link', { name: /^\/server-component$/ }).click();
26+
await page.getByText('Page (/server-component)').isVisible();
27+
await page.getByText('Stop transaction').click();
28+
29+
await serverComponentTransaction;
30+
});
31+
}

packages/e2e-tests/test-utils/event-proxy-server.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
138138

139139
export async function waitForRequest(
140140
proxyServerName: string,
141-
callback: (eventData: SentryRequestCallbackData) => boolean,
141+
callback: (eventData: SentryRequestCallbackData) => Promise<boolean> | boolean,
142142
): Promise<SentryRequestCallbackData> {
143143
const eventCallbackServerPort = await retrieveCallbackServerPort(proxyServerName);
144144

@@ -157,7 +157,20 @@ export async function waitForRequest(
157157
const eventCallbackData: SentryRequestCallbackData = JSON.parse(
158158
Buffer.from(eventContents, 'base64').toString('utf8'),
159159
);
160-
if (callback(eventCallbackData)) {
160+
const callbackResult = callback(eventCallbackData);
161+
if (typeof callbackResult !== 'boolean') {
162+
callbackResult.then(
163+
match => {
164+
if (match) {
165+
response.destroy();
166+
resolve(eventCallbackData);
167+
}
168+
},
169+
err => {
170+
throw err;
171+
},
172+
);
173+
} else if (callbackResult) {
161174
response.destroy();
162175
resolve(eventCallbackData);
163176
}
@@ -175,13 +188,13 @@ export async function waitForRequest(
175188

176189
export function waitForEnvelopeItem(
177190
proxyServerName: string,
178-
callback: (envelopeItem: EnvelopeItem) => boolean,
191+
callback: (envelopeItem: EnvelopeItem) => Promise<boolean> | boolean,
179192
): Promise<EnvelopeItem> {
180193
return new Promise((resolve, reject) => {
181-
waitForRequest(proxyServerName, eventData => {
194+
waitForRequest(proxyServerName, async eventData => {
182195
const envelopeItems = eventData.envelope[1];
183196
for (const envelopeItem of envelopeItems) {
184-
if (callback(envelopeItem)) {
197+
if (await callback(envelopeItem)) {
185198
resolve(envelopeItem);
186199
return true;
187200
}
@@ -191,11 +204,14 @@ export function waitForEnvelopeItem(
191204
});
192205
}
193206

194-
export function waitForError(proxyServerName: string, callback: (transactionEvent: Event) => boolean): Promise<Event> {
207+
export function waitForError(
208+
proxyServerName: string,
209+
callback: (transactionEvent: Event) => Promise<boolean> | boolean,
210+
): Promise<Event> {
195211
return new Promise((resolve, reject) => {
196-
waitForEnvelopeItem(proxyServerName, envelopeItem => {
212+
waitForEnvelopeItem(proxyServerName, async envelopeItem => {
197213
const [envelopeItemHeader, envelopeItemBody] = envelopeItem;
198-
if (envelopeItemHeader.type === 'event' && callback(envelopeItemBody as Event)) {
214+
if (envelopeItemHeader.type === 'event' && (await callback(envelopeItemBody as Event))) {
199215
resolve(envelopeItemBody as Event);
200216
return true;
201217
}
@@ -206,12 +222,12 @@ export function waitForError(proxyServerName: string, callback: (transactionEven
206222

207223
export function waitForTransaction(
208224
proxyServerName: string,
209-
callback: (transactionEvent: Event) => boolean,
225+
callback: (transactionEvent: Event) => Promise<boolean> | boolean,
210226
): Promise<Event> {
211227
return new Promise((resolve, reject) => {
212-
waitForEnvelopeItem(proxyServerName, envelopeItem => {
228+
waitForEnvelopeItem(proxyServerName, async envelopeItem => {
213229
const [envelopeItemHeader, envelopeItemBody] = envelopeItem;
214-
if (envelopeItemHeader.type === 'transaction' && callback(envelopeItemBody as Event)) {
230+
if (envelopeItemHeader.type === 'transaction' && (await callback(envelopeItemBody as Event))) {
215231
resolve(envelopeItemBody as Event);
216232
return true;
217233
}

0 commit comments

Comments
 (0)