From 5bf058aa5e530fefb9eeef5e52687c43a42ad8fd Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 17 Jan 2023 17:16:21 +0000 Subject: [PATCH] test(nextjs): Add NextJS server-side E2E tests. --- .../create-next-app/package.json | 3 +- .../create-next-app/pages/api/error.ts | 11 ++++ .../create-next-app/pages/api/hello.ts | 10 ---- .../create-next-app/pages/api/success.ts | 11 ++++ .../create-next-app/playwright.config.ts | 2 +- .../create-next-app/sentry.server.config.ts | 20 +++++++ .../create-next-app/test-recipe.json | 4 +- ...viour.test.ts => behaviour-client.test.ts} | 2 +- .../tests/behaviour-server.test.ts | 55 +++++++++++++++++++ 9 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 packages/e2e-tests/test-applications/create-next-app/pages/api/error.ts delete mode 100644 packages/e2e-tests/test-applications/create-next-app/pages/api/hello.ts create mode 100644 packages/e2e-tests/test-applications/create-next-app/pages/api/success.ts rename packages/e2e-tests/test-applications/create-next-app/tests/{client/behaviour.test.ts => behaviour-client.test.ts} (98%) create mode 100644 packages/e2e-tests/test-applications/create-next-app/tests/behaviour-server.test.ts diff --git a/packages/e2e-tests/test-applications/create-next-app/package.json b/packages/e2e-tests/test-applications/create-next-app/package.json index 4dc80476aee9..5d5e829bace6 100644 --- a/packages/e2e-tests/test-applications/create-next-app/package.json +++ b/packages/e2e-tests/test-applications/create-next-app/package.json @@ -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": { diff --git a/packages/e2e-tests/test-applications/create-next-app/pages/api/error.ts b/packages/e2e-tests/test-applications/create-next-app/pages/api/error.ts new file mode 100644 index 000000000000..5440074c39aa --- /dev/null +++ b/packages/e2e-tests/test-applications/create-next-app/pages/api/error.ts @@ -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 }); +} diff --git a/packages/e2e-tests/test-applications/create-next-app/pages/api/hello.ts b/packages/e2e-tests/test-applications/create-next-app/pages/api/hello.ts deleted file mode 100644 index eb4cc6657b37..000000000000 --- a/packages/e2e-tests/test-applications/create-next-app/pages/api/hello.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next'; - -type Data = { - name: string; -}; - -export default function handler(req: NextApiRequest, res: NextApiResponse) { - res.status(200).json({ name: 'John Doe' }); -} diff --git a/packages/e2e-tests/test-applications/create-next-app/pages/api/success.ts b/packages/e2e-tests/test-applications/create-next-app/pages/api/success.ts new file mode 100644 index 000000000000..6bd51d64db37 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-next-app/pages/api/success.ts @@ -0,0 +1,11 @@ +// 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 async function handler(req: NextApiRequest, res: NextApiResponse) { + await Sentry.flush(2000); + + res.status(200).json({ + transactionIds: global.transactionIds, + }); +} diff --git a/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts b/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts index 6bccee48f9fc..dabfa9c23619 100644 --- a/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts +++ b/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts @@ -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, }, }; diff --git a/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts b/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts index dfce34424f98..aa5a225f98f5 100644 --- a/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts +++ b/packages/e2e-tests/test-applications/create-next-app/sentry.server.config.ts @@ -4,6 +4,13 @@ import * as Sentry from '@sentry/nextjs'; +declare global { + namespace globalThis { + var transactionIds: [string | undefined]; + var exceptionId: string | undefined; + } +} + Sentry.init({ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, // Adjust this value in production, or use tracesSampler for greater control @@ -13,3 +20,16 @@ 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; +}); diff --git a/packages/e2e-tests/test-applications/create-next-app/test-recipe.json b/packages/e2e-tests/test-applications/create-next-app/test-recipe.json index b41a7a4fd05d..cf4bf4431929 100644 --- a/packages/e2e-tests/test-applications/create-next-app/test-recipe.json +++ b/packages/e2e-tests/test-applications/create-next-app/test-recipe.json @@ -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", diff --git a/packages/e2e-tests/test-applications/create-next-app/tests/client/behaviour.test.ts b/packages/e2e-tests/test-applications/create-next-app/tests/behaviour-client.test.ts similarity index 98% rename from packages/e2e-tests/test-applications/create-next-app/tests/client/behaviour.test.ts rename to packages/e2e-tests/test-applications/create-next-app/tests/behaviour-client.test.ts index a819664f35e3..d0ede66cf32f 100644 --- a/packages/e2e-tests/test-applications/create-next-app/tests/client/behaviour.test.ts +++ b/packages/e2e-tests/test-applications/create-next-app/tests/behaviour-client.test.ts @@ -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'); diff --git a/packages/e2e-tests/test-applications/create-next-app/tests/behaviour-server.test.ts b/packages/e2e-tests/test-applications/create-next-app/tests/behaviour-server.test.ts new file mode 100644 index 000000000000..a706926a565b --- /dev/null +++ b/packages/e2e-tests/test-applications/create-next-app/tests/behaviour-server.test.ts @@ -0,0 +1,55 @@ +import { test, expect } from '@playwright/test'; +import axios 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; + + console.log(`Polling for error eventId: ${exceptionId}`); + + expect + .poll( + async () => { + const response = await axios.get( + `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionId}/`, + { headers: { Authorization: `Bearer ${authToken}` } }, + ); + return response.status; + }, + { 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 | undefined) => { + if (!transactionId) { + return; + } + + await expect + .poll( + async () => { + const response = await axios.get( + `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionId}/`, + { headers: { Authorization: `Bearer ${authToken}` } }, + ); + return response.status; + }, + { timeout: EVENT_POLLING_TIMEOUT }, + ) + .toBe(200); + }), + ); +});