From 8dd56d18d80a5845c31884bf590f71b7deb4edd5 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 19 Jan 2026 11:03:32 +0100 Subject: [PATCH 1/2] add testing app --- .../nextjs-16-userfeedback/.gitignore | 48 ++++ .../nextjs-16-userfeedback/.npmrc | 4 + .../nextjs-16-userfeedback/README.md | 18 ++ .../app/examples/attachToFeedbackButton.tsx | 32 +++ .../app/examples/crashReportButton.tsx | 20 ++ .../app/examples/createFeedbackFormButton.tsx | 36 +++ .../app/examples/feedbackButton.tsx | 60 +++++ .../app/examples/myFeedbackForm.tsx | 48 ++++ .../app/examples/thumbsUpDownButtons.tsx | 53 +++++ .../app/examples/toggleFeedbackButton.tsx | 35 +++ .../app/examples/translatedFeedbackForm.tsx | 52 +++++ .../app/global-error.tsx | 19 ++ .../nextjs-16-userfeedback/app/globals.css | 62 +++++ .../nextjs-16-userfeedback/app/layout.tsx | 19 ++ .../nextjs-16-userfeedback/app/page.tsx | 69 ++++++ .../instrumentation-client.ts | 20 ++ .../nextjs-16-userfeedback/instrumentation.ts | 13 ++ .../nextjs-16-userfeedback/next-env.d.ts | 6 + .../nextjs-16-userfeedback/next.config.js | 8 + .../nextjs-16-userfeedback/package.json | 32 +++ .../playwright.config.mjs | 25 ++ .../nextjs-16-userfeedback/postcss.config.mjs | 5 + .../sentry.edge.config.ts | 12 + .../sentry.server.config.ts | 12 + .../start-event-proxy.mjs | 14 ++ .../tests/feedback.test.ts | 216 ++++++++++++++++++ .../nextjs-16-userfeedback/tsconfig.json | 38 +++ 27 files changed, 976 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/README.md create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/attachToFeedbackButton.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/crashReportButton.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/createFeedbackFormButton.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/feedbackButton.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/myFeedbackForm.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/thumbsUpDownButtons.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/toggleFeedbackButton.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/translatedFeedbackForm.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/global-error.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/globals.css create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/layout.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/page.tsx create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/instrumentation-client.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/instrumentation.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/next-env.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/next.config.js create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/package.json create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/postcss.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/sentry.edge.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/sentry.server.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/tests/feedback.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.gitignore b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.gitignore new file mode 100644 index 000000000000..0c60c8eeaee8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.gitignore @@ -0,0 +1,48 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +!*.d.ts + +# Sentry +.sentryclirc + +.vscode + +test-results +event-dumps + +.tmp_dev_server_logs diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc new file mode 100644 index 000000000000..a3160f4de175 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/.npmrc @@ -0,0 +1,4 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 +public-hoist-pattern[]=*import-in-the-middle* +public-hoist-pattern[]=*require-in-the-middle* diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/README.md b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/README.md new file mode 100644 index 000000000000..0bd38af41162 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/README.md @@ -0,0 +1,18 @@ +# Next.js 16 User Feedback E2E Tests + +This test application verifies the Sentry User Feedback SDK functionality with Next.js 16. + +## Tests + +The tests cover various feedback APIs: + +- `attachTo()` - Attaching feedback to custom buttons +- `createWidget()` - Creating/removing feedback widget triggers +- `createForm()` - Creating feedback forms with custom labels +- `captureFeedback()` - Programmatic feedback submission +- ThumbsUp/ThumbsDown sentiment tagging +- Dialog cancellation + +## Credits + +Shoutout to [Ryan Albrecht](https://github.com/ryan953) for the underlying [testing app](https://github.com/ryan953/nextjs-test-feedback)! diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/attachToFeedbackButton.tsx b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/attachToFeedbackButton.tsx new file mode 100644 index 000000000000..4dbfaec7dbc5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/attachToFeedbackButton.tsx @@ -0,0 +1,32 @@ +'use client'; + +import { useEffect, useState, useRef } from 'react'; +import * as Sentry from '@sentry/nextjs'; + +export default function AttachToFeedbackButton() { + const [feedback, setFeedback] = useState>(); + // Read `getFeedback` on the client only, to avoid hydration errors when server rendering + useEffect(() => { + setFeedback(Sentry.getFeedback()); + }, []); + + const buttonRef = useRef(null); + useEffect(() => { + if (feedback && buttonRef.current) { + const unsubscribe = feedback.attachTo(buttonRef.current, { + tags: { component: 'AttachToFeedbackButton' }, + onSubmitSuccess: data => { + console.log('onSubmitSuccess', data); + }, + }); + return unsubscribe; + } + return () => {}; + }, [feedback]); + + return ( + + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/crashReportButton.tsx b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/crashReportButton.tsx new file mode 100644 index 000000000000..5a85c441e994 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/crashReportButton.tsx @@ -0,0 +1,20 @@ +'use client'; + +import * as Sentry from '@sentry/nextjs'; + +export default function CrashReportButton() { + return ( + + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/createFeedbackFormButton.tsx b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/createFeedbackFormButton.tsx new file mode 100644 index 000000000000..3514b1baa95c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/createFeedbackFormButton.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import * as Sentry from '@sentry/nextjs'; + +type FeedbackIntegration = ReturnType; + +export default function CreateFeedbackFormButton() { + const [feedback, setFeedback] = useState(); + // Read `getFeedback` on the client only, to avoid hydration errors when server rendering + useEffect(() => { + setFeedback(Sentry.getFeedback()); + }, []); + + // Don't render custom feedback button if Feedback integration isn't installed + if (!feedback) { + return null; + } + + return ( + + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/feedbackButton.tsx b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/feedbackButton.tsx new file mode 100644 index 000000000000..d77bd6199069 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/feedbackButton.tsx @@ -0,0 +1,60 @@ +'use client'; + +import type { RefObject } from 'react'; +import * as Sentry from '@sentry/nextjs'; +import { useEffect, useRef, useState } from 'react'; + +export default function FeedbackButton() { + const buttonRef = useRef(null); + useFeedbackWidget({ + buttonRef, + options: { + tags: { + component: 'FeedbackButton', + }, + }, + }); + + return ( + + ); +} + +function useFeedbackWidget({ + buttonRef, + options = {}, +}: { + buttonRef?: RefObject | RefObject; + options?: { + tags?: Record; + }; +}) { + const [feedback, setFeedback] = useState>(); + // Read `getFeedback` on the client only, to avoid hydration errors when server rendering + useEffect(() => { + setFeedback(Sentry.getFeedback()); + }, []); + + useEffect(() => { + if (!feedback) { + return undefined; + } + + if (buttonRef) { + if (buttonRef.current) { + return feedback.attachTo(buttonRef.current, options); + } + } else { + const widget = feedback.createWidget(options); + return () => { + widget.removeFromDom(); + }; + } + + return undefined; + }, [buttonRef, feedback, options]); + + return feedback; +} diff --git a/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/myFeedbackForm.tsx b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/myFeedbackForm.tsx new file mode 100644 index 000000000000..8f693f7e4fb0 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nextjs-16-userfeedback/app/examples/myFeedbackForm.tsx @@ -0,0 +1,48 @@ +'use client'; + +import * as Sentry from '@sentry/nextjs'; + +export default function MyFeedbackForm() { + return ( +
{ + event.preventDefault(); + const formData = new FormData(event.currentTarget); + + const attachment = async () => { + const attachmentField = formData.get('attachment') as File; + if (!attachmentField || attachmentField.size === 0) { + return null; + } + const data = new Uint8Array(await attachmentField.arrayBuffer()); + const attachmentData = { + data, + filename: 'upload', + }; + return attachmentData; + }; + + Sentry.getCurrentScope().setTags({ component: 'MyFeedbackForm' }); + const attachmentData = await attachment(); + Sentry.captureFeedback( + { + name: String(formData.get('name')), + email: String(formData.get('email')), + message: String(formData.get('message')), + }, + attachmentData ? { attachments: [attachmentData] } : undefined, + ); + }} + > + + +