Skip to content

Commit

Permalink
test(nextjs): Add NextJS server-side E2E tests. (#6829)
Browse files Browse the repository at this point in the history
  • Loading branch information
onurtemizkan authored Jan 23, 2023
1 parent bee8d52 commit d1ddeff
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "TEST_MODE=build playwright test",
"test": "test:prod && test:dev",
"test:prod": "TEST_MODE=prod playwright test",
"test:dev": "TEST_MODE=dev playwright test"
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as Sentry from '@sentry/nextjs';
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const exceptionId = Sentry.captureException(new Error('This is an error'));

await Sentry.flush(2000);

res.status(200).json({ exceptionId });
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import * as Sentry from '@sentry/nextjs';

export default function handler(req: NextApiRequest, res: NextApiResponse) {
const transaction = Sentry.startTransaction({ name: 'test-transaction', op: 'e2e-test' });
Sentry.getCurrentHub().configureScope(scope => scope.setSpan(transaction));

const span = transaction.startChild();

span.finish();
transaction.finish();

Sentry.flush().then(() => {
res.status(200).json({
transactionIds: global.transactionIds,
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const config: PlaywrightTestConfig = {

/* Run your local dev server before starting the tests */
webServer: {
command: process.env.TEST_MODE === 'build' ? 'yarn start' : 'yarn dev',
command: process.env.TEST_MODE === 'prod' ? 'yarn start' : 'yarn dev',
port: 3000,
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

import * as Sentry from '@sentry/nextjs';

declare global {
namespace globalThis {
var transactionIds: string[];
}
}

Sentry.init({
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
// Adjust this value in production, or use tracesSampler for greater control
Expand All @@ -13,3 +19,17 @@ Sentry.init({
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
// that it will also get attached to your source maps
});

Sentry.addGlobalEventProcessor(event => {
global.transactionIds = global.transactionIds || [];

if (event.type === 'transaction') {
const eventId = event.event_id;

if (eventId) {
global.transactionIds.push(eventId);
}
}

return event;
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"buildCommand": "yarn install --pure-lockfile && npx playwright install && yarn build",
"tests": [
{
"testName": "Playwright tests - Build Mode",
"testCommand": "yarn test"
"testName": "Playwright tests - Prod Mode",
"testCommand": "yarn test:prod"
},
{
"testName": "Playwright tests - Dev Mode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
const EVENT_POLLING_TIMEOUT = 30_000;

test('Sends an exception to Sentry', async ({ page, baseURL }) => {
test('Sends a client-side exception to Sentry', async ({ page }) => {
await page.goto('/');

const exceptionButton = page.locator('id=exception-button');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { test, expect } from '@playwright/test';
import axios, { AxiosError } from 'axios';

const authToken = process.env.E2E_TEST_AUTH_TOKEN;
const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
const EVENT_POLLING_TIMEOUT = 30_000;

test('Sends a server-side exception to Sentry', async ({ baseURL }) => {
const { data } = await axios.get(`${baseURL}/api/error`);
const { exceptionId } = data;

const url = `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionId}/`;

console.log(`Polling for error eventId: ${exceptionId}`);

await expect
.poll(
async () => {
try {
const response = await axios.get(url, { headers: { Authorization: `Bearer ${authToken}` } });

return response.status;
} catch (e) {
if (e instanceof AxiosError && e.response) {
if (e.response.status !== 404) {
throw e;
} else {
return e.response.status;
}
} else {
throw e;
}
}
},
{ timeout: EVENT_POLLING_TIMEOUT },
)
.toBe(200);
});

test('Sends server-side transactions to Sentry', async ({ baseURL }) => {
const { data } = await axios.get(`${baseURL}/api/success`);
const { transactionIds } = data;

console.log(`Polling for transaction eventIds: ${JSON.stringify(transactionIds)}`);

await Promise.all(
transactionIds.map(async (transactionId: string) => {
const url = `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionId}/`;

await expect
.poll(
async () => {
try {
const response = await axios.get(url, { headers: { Authorization: `Bearer ${authToken}` } });

return response.status;
} catch (e) {
if (e instanceof AxiosError && e.response) {
if (e.response.status !== 404) {
throw e;
} else {
return e.response.status;
}
} else {
throw e;
}
}
},
{ timeout: EVENT_POLLING_TIMEOUT },
)
.toBe(200);
}),
);
});

0 comments on commit d1ddeff

Please sign in to comment.